summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohnMacFarlane <>2018-11-13 17:30:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2018-11-13 17:30:00 (GMT)
commitd58d43806c1041f03569ed3f21dfc5a236d4b31d (patch)
tree01cfb18bc68584ee82676368d52486bd5ca97492
parent941a9fafc0b0713fdcbfbb41f8eb3fa35556a074 (diff)
version 0.150.15
-rw-r--r--changelog36
-rw-r--r--locales/locales-el-GR.xml8
-rw-r--r--locales/locales-fi-FI.xml4
-rw-r--r--man/man1/pandoc-citeproc.1107
-rw-r--r--pandoc-citeproc.cabal2
-rw-r--r--src/Text/CSL/Eval.hs2
-rw-r--r--src/Text/CSL/Eval/Date.hs16
-rw-r--r--src/Text/CSL/Eval/Names.hs5
-rw-r--r--src/Text/CSL/Input/Bibtex.hs26
-rw-r--r--src/Text/CSL/Pandoc.hs267
-rw-r--r--src/Text/CSL/Proc/Disamb.hs77
-rw-r--r--src/Text/CSL/Reference.hs117
-rw-r--r--stack.yaml21
-rw-r--r--tests/biblio2yaml/266.biblatex28
-rw-r--r--tests/issue356.expected.native5
-rw-r--r--tests/issue356.in.native2
-rw-r--r--tests/issue361.expected.native12
-rw-r--r--tests/issue361.in.native12
-rw-r--r--tests/issue365.expected.native5
-rw-r--r--tests/issue365.in.native2
-rw-r--r--tests/le-tapuscrit-note.csl496
-rw-r--r--tests/locators-delimited.expected.native20
-rw-r--r--tests/locators-delimited.in.native20
-rw-r--r--tests/locators-integrated.expected.native26
-rw-r--r--tests/locators-integrated.in.native26
-rw-r--r--tests/locators.csl68
26 files changed, 1186 insertions, 224 deletions
diff --git a/changelog b/changelog
index 49c95c7..71304ea 100644
--- a/changelog
+++ b/changelog
@@ -1,3 +1,39 @@
+pandoc-citeproc (0.15)
+
+ * Support BibLaTeX extended name format (#266).
+ * Add refOtherIds field to Reference, and populate it with
+ the 'ids' field in biblatex (#356). Search on refOtherIds
+ as well as refId when looking for references.
+ * Allow the extent of the locator to be explicitly indicated
+ using curly braces, as in: [@key{67}], [@key, {p. 95 A 3}, suffix]
+ (Cormac Relf).
+ * Parse balanced brackets in locators like '1(2)(a)(i)' (Cormac Relf).
+ This is useful for citing legislation. It is necessary to be more liberal
+ in locator parsing because some styles put other elements after a locator,
+ like short titles in the case of AGLC, and if you can't parse a locator,
+ it gets rendered after, or worse, split in half.
+ * Drop ibid-with-locator when prev cite has same locator and label (#241,
+ Cormac Relf).
+ ```
+ [@key] => "first"
+ [@key] => "ibid"
+ [@key, 53] => "ibid-with-locator"
+ [@key, 123] => "ibid-with-locator"
+ [@key, 123] => "ibid"
+ [@key] => "subsequent"
+ ```
+ See http://docs.citationstyles.org/en/stable/specification.html#locators
+ * Fix bug wherein nonbreaking spaces classed a name as East Asian,
+ so that spaces aren't used between given and family (#365).
+ Also regularized logic for detecting "byzantine" names
+ (those that get the space). Now a name is treated as
+ byzantine if there are any byzantine characters in it.
+ Previously some checks required all byzantine characters.
+ * Add mktest.sh for wrangling test cases (#364, Cormac Relf).
+ * Update pandoc-citeproc man page.
+ * Update locales el-GR, fi-FI.
+ * Add CONTRIBUTING.md.
+
pandoc-citeproc (0.14.8.1)
* Avoid putting a space after nonbreaking space at end of prefix.
diff --git a/locales/locales-el-GR.xml b/locales/locales-el-GR.xml
index 56835ce..9de96c5 100644
--- a/locales/locales-el-GR.xml
+++ b/locales/locales-el-GR.xml
@@ -172,12 +172,12 @@
<term name="note" form="short">σημ.</term>
<term name="opus" form="short">έργ.</term>
<term name="page" form="short">
- <single>σ</single>
- <multiple>σσ</multiple>
+ <single>σ.</single>
+ <multiple>σσ.</multiple>
</term>
<term name="number-of-pages" form="short">
- <single>σ</single>
- <multiple>σσ</multiple>
+ <single>σ.</single>
+ <multiple>σσ.</multiple>
</term>
<term name="paragraph" form="short">παρ.</term>
<term name="part" form="short">μέρ.</term>
diff --git a/locales/locales-fi-FI.xml b/locales/locales-fi-FI.xml
index 7b3468b..bd09f6e 100644
--- a/locales/locales-fi-FI.xml
+++ b/locales/locales-fi-FI.xml
@@ -70,8 +70,8 @@
<term name="version">versio</term>
<!-- ANNO DOMINI; BEFORE CHRIST -->
- <term name="ad">eaa.</term>
- <term name="bc">jaa.</term>
+ <term name="ad">jaa.</term>
+ <term name="bc">eaa.</term>
<!-- PUNCTUATION -->
<term name="open-quote">”</term>
diff --git a/man/man1/pandoc-citeproc.1 b/man/man1/pandoc-citeproc.1
index b34873c..2e8b545 100644
--- a/man/man1/pandoc-citeproc.1
+++ b/man/man1/pandoc-citeproc.1
@@ -1,7 +1,7 @@
.\"t
.\" Automatically generated by Pandoc 2.4
.\"
-.TH "pandoc\-citeproc" "1" "2018-10-19" "pandoc-citeproc 0.14.7" ""
+.TH "pandoc\-citeproc" "1" "2018-11-13" "pandoc-citeproc 0.15" ""
.hy
.SH NAME
.PP
@@ -34,7 +34,7 @@ To process citations with pandoc, call pandoc\-citeproc as a filter:
.IP
.nf
\f[C]
-pandoc\ \-\-filter\ pandoc\-citeproc\ input.md\ \-s\ \-o\ output.html
+pandoc \-\-filter pandoc\-citeproc input.md \-s \-o output.html
\f[R]
.fi
.PP
@@ -117,8 +117,8 @@ T}@T{
T}
.TE
.PP
-Note that \f[C]\&.bib\f[R] can generally be used with both BibTeX and
-BibLaTeX files, but you can use \f[C]\&.bibtex\f[R] to force BibTeX.
+Note that \f[C].bib\f[R] can generally be used with both BibTeX and
+BibLaTeX files, but you can use \f[C].bibtex\f[R] to force BibTeX.
.RE
.TP
.B \f[C]references\f[R]
@@ -130,17 +130,17 @@ Here is an example:
.IP
.nf
\f[C]
-\-\ id:\ doe2006
-\ \ author:
-\ \ \ \ family:\ Doe
-\ \ \ \ given:\ [John,\ F.]
-\ \ title:\ Article
-\ \ page:\ 33\-34
-\ \ issued:
-\ \ \ \ year:\ 2006
-\ \ type:\ article\-journal
-\ \ volume:\ 6
-\ \ container\-title:\ Journal\ of\ Generic\ Studies
+\- id: doe2006
+ author:
+ family: Doe
+ given: [John, F.]
+ title: Article
+ page: 33\-34
+ issued:
+ year: 2006
+ type: article\-journal
+ volume: 6
+ container\-title: Journal of Generic Studies
\f[R]
.fi
.PP
@@ -160,15 +160,11 @@ If this is left off, pandoc\-citeproc will look for
\f[C]$HOME/.pandoc/default.csl\f[R], and if this is not present, it will
use \f[C]chicago\-author\-date.csl\f[R], looking first in
\f[C]$HOME/.csl\f[R] and then in its own data files.
-.RS
-.RE
.TP
.B \f[C]link\-citations\f[R]
If this has a true value, citations in author\-date and numerical styles
will be hyperlinked to their corresponding bibliography entries.
The default is not to add hyperlinks.
-.RS
-.RE
.TP
.B \f[C]citation\-abbreviations\f[R]
Path to a CSL abbreviations JSON file.
@@ -178,7 +174,7 @@ pandoc\-citeproc will look in the \f[C]$HOME/.csl\f[R] directory (or
Windows 7).
The format is described
here (http://citationstylist.org/2011/10/19/abbreviations-for-zotero-test-release).
-Abbreviations are only output if, in the \f[C]\&.csl\f[R] file,
+Abbreviations are only output if, in the \f[C].csl\f[R] file,
\f[C]form=\[dq]short\[dq]\f[R] is set on the element that renders the
variable.
.RS
@@ -187,13 +183,13 @@ Here is a short example:
.IP
.nf
\f[C]
-{\ \[dq]default\[dq]:\ {
-\ \ \ \ \[dq]container\-title\[dq]:\ {
-\ \ \ \ \ \ \ \ \ \ \ \ \[dq]Lloyd\[aq]s\ Law\ Reports\[dq]:\ \[dq]Lloyd\[aq]s\ Rep\[dq],
-\ \ \ \ \ \ \ \ \ \ \ \ \[dq]Estates\ Gazette\[dq]:\ \[dq]EG\[dq],
-\ \ \ \ \ \ \ \ \ \ \ \ \[dq]Scots\ Law\ Times\[dq]:\ \[dq]SLT\[dq]
-\ \ \ \ }
-\ \ }
+{ \[dq]default\[dq]: {
+ \[dq]container\-title\[dq]: {
+ \[dq]Lloyd\[aq]s Law Reports\[dq]: \[dq]Lloyd\[aq]s Rep\[dq],
+ \[dq]Estates Gazette\[dq]: \[dq]EG\[dq],
+ \[dq]Scots Law Times\[dq]: \[dq]SLT\[dq]
+ }
+ }
}
\f[R]
.fi
@@ -208,16 +204,12 @@ metadata or the CSL file.
(For backwards compatibility, the field \f[C]locale\f[R] can be used
instead of \f[C]lang\f[R], but this \f[C]lang\f[R] should be used going
forward.)
-.RS
-.RE
.TP
.B \f[C]suppress\-bibliography\f[R]
If this has a true value, the bibliography will be left off.
Otherwise a bibliography will be inserted into each Div element with id
\f[C]refs\f[R].
If there is no such Div, one will be created at the end of the document.
-.RS
-.RE
.TP
.B \f[C]reference\-section\-title\f[R]
If this has a value, a section header with this title will be added
@@ -225,20 +217,15 @@ before the bibliography.
If \f[C]reference\-section\-title\f[R] is not specified and the document
ends with a section header, this final header will be treated as the
bibliography header.
-.RS
-.RE
.TP
.B \f[C]notes\-after\-punctuation\f[R]
If true (the default), pandoc will put footnote citations after
following punctuation.
-For example, if the source contains
-\f[C]blah\ blah\ [\[at]jones99].\f[R], the result will look like
-\f[C]blah\ blah.[\[ha]1]\f[R], with the note moved after the period and
-the space collapsed.
+For example, if the source contains \f[C]blah blah [\[at]jones99].\f[R],
+the result will look like \f[C]blah blah.[\[ha]1]\f[R], with the note
+moved after the period and the space collapsed.
If false, the space will still be collapsed, but the footnote will not
be moved after the punctuation.
-.RS
-.RE
.PP
The metadata must contain either \f[C]references\f[R] or
\f[C]bibliography\f[R] or both as a source of references.
@@ -280,15 +267,11 @@ However, stylesheets customized in this way will not be useable by other
CSL implementations.
.SH OPTIONS
.TP
-.B \f[C]\-y,\ \-\-bib2yaml\f[R]
+.B \f[C]\-y, \-\-bib2yaml\f[R]
Convert bibliography to YAML suitable for inclusion in pandoc metadata.
-.RS
-.RE
.TP
-.B \f[C]\-j,\ \-\-bib2json\f[R]
+.B \f[C]\-j, \-\-bib2json\f[R]
Convert bibliography to CSL JSON suitable for import into Zotero.
-.RS
-.RE
.TP
.B \f[C]\-f\f[R] \f[I]FORMAT\f[R], \f[C]\-\-format=\f[R]\f[I]FORMAT\f[R]
Specify format of bibliography to be converted.
@@ -296,36 +279,24 @@ Legal values are \f[C]biblatex\f[R], \f[C]bibtex\f[R], \f[C]ris\f[R],
\f[C]endnote\f[R], \f[C]endnotexml\f[R], \f[C]isi\f[R],
\f[C]medline\f[R], \f[C]copac\f[R], \f[C]mods\f[R], \f[C]nbib\f[R], and
\f[C]json\f[R].
-.RS
-.RE
.TP
-.B \f[C]\-h,\ \-\-help\f[R]
+.B \f[C]\-h, \-\-help\f[R]
Print usage information.
-.RS
-.RE
.TP
.B \f[C]\-\-man\f[R]
Print the man page in groff man format.
To get a plain text version,
-\f[C]pandoc\-citeproc\ \-\-man\ |\ groff\ \-mman\ \-Tutf8\f[R].
-To \f[C]pandoc\-citeproc\ \-\-man\ |\ groff\ \-mman\ \-Thtml\f[R].
-.RS
-.RE
+\f[C]pandoc\-citeproc \-\-man | groff \-mman \-Tutf8\f[R].
+To \f[C]pandoc\-citeproc \-\-man | groff \-mman \-Thtml\f[R].
.TP
.B \f[C]\-\-license\f[R]
Print the license.
-.RS
-.RE
.TP
-.B \f[C]\-q,\ \-\-quiet\f[R]
+.B \f[C]\-q, \-\-quiet\f[R]
Silence all warnings.
-.RS
-.RE
.TP
-.B \f[C]\-V,\ \-\-version\f[R]
+.B \f[C]\-V, \-\-version\f[R]
Print version.
-.RS
-.RE
.SH NOTES
.SS General
.PP
@@ -343,7 +314,7 @@ Particularly relevant are
fields) and
<http://citationstyles.org/downloads/specification.html#appendix-iv-variables>
(which does contain comments).
-.SS Titles: Title vs.\~Sentence Case
+.SS Titles: Title vs.\ Sentence Case
.PP
If you are using a bibtex or biblatex bibliography, then observe the
following rules:
@@ -364,7 +335,7 @@ For example:
.IP
.nf
\f[C]
-title\ =\ {My\ Dinner\ with\ {Andre}}
+title = {My Dinner with {Andre}}
\f[R]
.fi
.RE
@@ -375,7 +346,7 @@ protected:
.IP
.nf
\f[C]
-title\ =\ {Spin\ Wave\ Dispersion\ on\ the\ {nm}\ Scale}
+title = {Spin Wave Dispersion on the {nm} Scale}
\f[R]
.fi
.PP
@@ -402,11 +373,11 @@ syntax:
.IP
.nf
\f[C]
-Spin\ wave\ dispersion\ on\ the\ <span\ class=\[dq]nocase\[dq]>nm</span>\ scale
+Spin wave dispersion on the <span class=\[dq]nocase\[dq]>nm</span> scale
\f[R]
.fi
.RE
-.SS Conference Papers, Published vs.\~Unpublished
+.SS Conference Papers, Published vs.\ Unpublished
.PP
For a formally published conference paper, use the biblatex entry type
\f[C]inproceedings\f[R] (which will be mapped to CSL
@@ -421,7 +392,7 @@ use the biblatex entry type \f[C]unpublished\f[R] with an
\f[C]eventtitle\f[R] field (this entry type will be mapped to CSL
\f[C]speech\f[R]).
Use the biblatex \f[C]type\f[R] field to indicate the type,
-e.g.\~\[lq]Paper\[rq], or \[lq]Poster\[rq].
+e.g.\ \[lq]Paper\[rq], or \[lq]Poster\[rq].
\f[C]venue\f[R] and \f[C]eventdate\f[R] may be useful too, though
\f[C]eventdate\f[R] will not be rendered by most CSL styles.
Note that \f[C]venue\f[R] is for the event\[cq]s venue, unlike
diff --git a/pandoc-citeproc.cabal b/pandoc-citeproc.cabal
index 7d82d28..2f511ca 100644
--- a/pandoc-citeproc.cabal
+++ b/pandoc-citeproc.cabal
@@ -1,5 +1,5 @@
name: pandoc-citeproc
-version: 0.14.8.1
+version: 0.15
cabal-version: 1.12
synopsis: Supports using pandoc with citeproc
diff --git a/src/Text/CSL/Eval.hs b/src/Text/CSL/Eval.hs
index 6fb1293..1cbd031 100644
--- a/src/Text/CSL/Eval.hs
+++ b/src/Text/CSL/Eval.hs
@@ -375,7 +375,7 @@ formatNumber f fm v n
_ -> maybe "" show . (safeRead :: String -> Maybe Int)
roman :: Int -> String
- roman = foldr (++) [] . reverse . map (uncurry (!!)) . zip romanList .
+ roman = concat . reverse . zipWith (!!) romanList .
map (readNum . return) . take 4 .
reverse . show
romanList = [[ "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" ]
diff --git a/src/Text/CSL/Eval/Date.hs b/src/Text/CSL/Eval/Date.hs
index 6a746cc..fde8617 100644
--- a/src/Text/CSL/Eval/Date.hs
+++ b/src/Text/CSL/Eval/Date.hs
@@ -175,19 +175,23 @@ formatDate em k tm dp date
ordinal :: [CslTerm] -> String -> Int -> String
ordinal ts v s
| s < 10 = let a = termPlural (getWith1 (show s)) in
- if a == [] then setOrd (term []) else show s ++ a
+ if null a then setOrd (term []) else show s ++ a
| s < 100 = let a = termPlural (getWith2 (show s))
b = getWith1 [last (show s)] in
- if a /= []
+ if not (null a)
then show s ++ a
- else if termPlural b == [] || (termMatch b /= [] && termMatch b /= "last-digit")
- then setOrd (term []) else setOrd b
+ else if null (termPlural b) ||
+ (not (null (termMatch b)) &&
+ termMatch b /= "last-digit")
+ then setOrd (term [])
+ else setOrd b
| otherwise = let a = getWith2 last2
b = getWith1 [last (show s)] in
- if termPlural a /= [] && termMatch a /= "whole-number"
+ if not (null (termPlural a)) &&
+ termMatch a /= "whole-number"
then setOrd a
else if null (termPlural b) ||
- (termMatch b /= [] &&
+ (not (null (termMatch b)) &&
termMatch b /= "last-digit")
then setOrd (term [])
else setOrd b
diff --git a/src/Text/CSL/Eval/Names.hs b/src/Text/CSL/Eval/Names.hs
index 0a8573b..296f432 100644
--- a/src/Text/CSL/Eval/Names.hs
+++ b/src/Text/CSL/Eval/Names.hs
@@ -278,7 +278,7 @@ formatName m b f fm ops np n
$ splitStrWhen (=='-') x
sortSep g s = when_ g $ separator ++ addAffixes (g <+> s) "given" mempty
- separator = if any isByzantine (stringify family)
+ separator = if isByzantineFamily
then
if null (getOptionVal "sort-separator" ops)
then [OPan [Str ",", Space]]
@@ -323,6 +323,7 @@ formatName m b f fm ops np n
(c >= '\x021a' && c <= '\x021b') ||
(c >= '\x202a' && c <= '\x202e')
+ isByzantineFamily = any isByzantine (stringify family)
shortName = oPan' (unFormatted $ nondropping <+> family) (form "family")
longName g
@@ -338,7 +339,7 @@ formatName m b f fm ops np n
in oPan' (unFormatted fam) (form "family") <> sortSep g par <> suffCom
| otherwise = let fam = addAffixes (dropping <+> nondropping <+> family) "family" suff
gvn = oPan' (unFormatted g) (form "given")
- in if all isByzantine $ stringify $ unFormatted family
+ in if isByzantineFamily
then gvn <++> fam
else fam <> gvn
diff --git a/src/Text/CSL/Input/Bibtex.hs b/src/Text/CSL/Input/Bibtex.hs
index 12f1735..b9e7a1b 100644
--- a/src/Text/CSL/Input/Bibtex.hs
+++ b/src/Text/CSL/Input/Bibtex.hs
@@ -38,7 +38,7 @@ import Text.CSL.Compat.Pandoc (readLaTeX)
import Text.CSL.Exception (CiteprocException (ErrorReadingBib, ErrorReadingBibFile))
import Text.CSL.Parser (parseLocale)
import Text.CSL.Reference
-import Text.CSL.Style (Agent (..), CslTerm (..),
+import Text.CSL.Style (Agent (..), emptyAgent, CslTerm (..),
Formatted (..), Locale (..))
import Text.CSL.Util (onBlocks, protectCase, safeRead,
splitStrWhen, trim, unTitlecase)
@@ -1009,7 +1009,7 @@ toLiteralList _ = mzero
toAuthorList :: Options -> [Block] -> Bib [Agent]
toAuthorList opts [Para xs] =
- mapM (toAuthor opts) $ splitByAnd xs
+ filter (/= emptyAgent) <$> mapM (toAuthor opts) (splitByAnd xs)
toAuthorList opts [Plain xs] = toAuthorList opts [Para xs]
toAuthorList _ _ = mzero
@@ -1036,6 +1036,25 @@ toAuthor _ [Span ("",[],[]) ils] =
, commaSuffix = False
, parseNames = False
}
+ -- extended BibLaTeX name format - see #266
+toAuthor _ ils@(Str ys:_) | '=' `elem` ys = do
+ let commaParts = splitWhen (== Str ",")
+ $ splitStrWhen (\c -> c == ',' || c == '=' || c == '\160')
+ $ ils
+ let addPart ag (Str "given" : Str "=" : xs) =
+ ag{ givenName = givenName ag ++ [Formatted xs] }
+ addPart ag (Str "family" : Str "=" : xs) =
+ ag{ familyName = Formatted xs }
+ addPart ag (Str "prefix" : Str "=" : xs) =
+ ag{ droppingPart = Formatted xs }
+ addPart ag (Str "useprefix" : Str "=" : Str "true" : _) =
+ ag{ nonDroppingPart = droppingPart ag, droppingPart = mempty }
+ addPart ag (Str "suffix" : Str "=" : xs) =
+ ag{ nameSuffix = Formatted xs }
+ addPart ag (Space : xs) = addPart ag xs
+ addPart ag _ = ag
+ return $ foldl' addPart emptyAgent commaParts
+
-- First von Last
-- von Last, First
-- von Last, Jr ,First
@@ -1227,6 +1246,8 @@ itemToReference lang locale bibtex caseTransform = bib $ do
Lang "en" _ -> caseTransform
_ -> False }
id' <- asks identifier
+ otherIds <- (map trim . splitWhen (==',') <$> getRawField "ids")
+ <|> return []
et <- asks entryType
guard $ et /= "xdata"
opts <- (parseOptions <$> getRawField "options") <|> return []
@@ -1521,6 +1542,7 @@ itemToReference lang locale bibtex caseTransform = bib $ do
return $ emptyReference
{ refId = Literal id'
+ , refOtherIds = map Literal otherIds
, refType = reftype
, author = author'
, editor = editor'
diff --git a/src/Text/CSL/Pandoc.hs b/src/Text/CSL/Pandoc.hs
index e357c2d..479afd6 100644
--- a/src/Text/CSL/Pandoc.hs
+++ b/src/Text/CSL/Pandoc.hs
@@ -90,8 +90,7 @@ mkNoteMap doc =
splitUp :: [(Int, [String])] -> [(Int, String)]
splitUp = concatMap (\(n,ss) -> map (n,) ss)
go :: (Int, String) -> M.Map String Int -> M.Map String Int
- go (notenumber, citeid) notemap =
- M.insert citeid notenumber notemap
+ go (notenumber, citeid) = M.insert citeid notenumber
-- if document contains a Div with id="refs", insert
-- references as its contents. Otherwise, insert references
@@ -157,8 +156,7 @@ isYesValue _ = False
-- if the 'nocite' Meta field contains a citation with id = '*',
-- create a cite with to all the references.
mkNociteWildcards :: [Reference] -> [[Citation]] -> [[Citation]]
-mkNociteWildcards refs nocites =
- map expandStar nocites
+mkNociteWildcards refs = map expandStar
where expandStar cs =
case [c | c <- cs
, citationId c == "*"] of
@@ -271,7 +269,7 @@ decodeEntities ('&':xs) =
#if MIN_VERSION_tagsoup(0,13,0)
Just s -> s ++ decodeEntities ws
#else
- Just c -> [c] ++ decodeEntities ws
+ Just c -> c : decodeEntities ws
#endif
Nothing -> '&' : decodeEntities xs
_ -> '&' : decodeEntities xs
@@ -389,7 +387,7 @@ comb f xs ys =
removeLeadingPunct zs = zs
in f xs' ++ ys
--- | Retrieve all citations from a 'Pandoc' docuument. To be used with
+-- | Retrieve all citations from a 'Pandoc' document. To be used with
-- 'query'.
getCitation :: Inline -> [[Citation]]
getCitation i | Cite t _ <- i = [t]
@@ -432,42 +430,195 @@ toCslCite locMap c
, CSL.citeHash = citationHash c
}
+splitInp :: [Inline] -> [Inline]
+splitInp = splitStrWhen (\c -> splitOn c || isSpace c)
+ where
+ splitOn ':' = False
+ splitOn c = isPunctuation c
+
locatorWords :: LocatorMap -> [Inline] -> (String, String, [Inline])
locatorWords locMap inp =
- case parse (pLocatorWords locMap) "suffix" $
- splitStrWhen (\c -> isLocatorPunct c || isSpace c) inp of
+ case parse (pLocatorWords locMap) "suffix" $ splitInp inp of
Right r -> r
Left _ -> ("","",inp)
+-- Some terminology
+-- ----------------
+-- Word => 89
+-- 12-15
+-- 13(a)(i)-(iv)
+-- [1.2.5]
+--
+-- Integrated => [@citekey, 89]
+-- [@citekey, p. 40, 41, 89-199, suffix]
+-- Delimited => [@citekey{89}]
+-- [@citekey, {p. literally anything except unbalanced curly quotes}, suffix]
+--
+-- When parsing integrated locators you have to be careful not to include
+-- 'suffix' in the locator, so that means pretty strict control over when
+-- you're allowed to use NO digits in a word. [@citekey, p. 40(a) (bcd)] will
+-- stop parsing the locator after (a). You also have to be careful not to parse
+-- random terms like 'and' in en-US as citeLabels, which means careful control
+-- over what must come directly after any label string (via notFollowedBy).
+--
+-- With delimited locators, it's a different story. Parse as long a locator
+-- label as you can find in the terms map, then include EVERYTHING in the outer
+-- {} braces.
+--
+-- Ultimately the complexity is driven by wanting as many locator words as
+-- possible being parsed in the integrated style, because it fits with the
+-- aims of Markdown (being readable). Ideally, anything except a word with
+-- neither roman numerals nor arabic digits can be integrated. Some
+-- counter-examples:
+-- a
+-- (a)(b)(c)
+-- (hello)
+
pLocatorWords :: LocatorMap -> Parsec [Inline] st (String, String, [Inline])
pLocatorWords locMap = do
- (la,lo) <- pLocator locMap
+ optional $ pMatchChar "," (== ',')
+ optional pSpace
+ (la, lo) <- pLocatorDelimited locMap <|> pLocatorIntegrated locMap
s <- getInput -- rest is suffix
- return (la, lo, s)
-
-pMatch :: (Inline -> Bool) -> Parsec [Inline] st Inline
-pMatch condition = try $ do
- t <- anyToken
- guard $ condition t
- return t
-
-pSpace :: Parsec [Inline] st Inline
-pSpace = pMatch (\t -> isSpacy t || t == Str "\160")
+ -- need to trim, otherwise "p. 9" and "9" will have 'different' locators later on
+ -- i.e. the first one will be " 9"
+ return (la, trim lo, s)
+
+pLocatorDelimited :: LocatorMap -> Parsec [Inline] st (String, String)
+pLocatorDelimited locMap = try $ do
+ _ <- pMatchChar "{" (== '{')
+ skipMany pSpace -- gobble pre-spaces so label doesn't try to include them
+ (la, _) <- pLocatorLabelDelimited locMap
+ -- we only care about balancing {} and [] (because of the outer [] scope);
+ -- the rest can be anything
+ let inner = do { t <- anyToken; return (True, stringify t) }
+ gs <- many (pBalancedBraces [('{','}'), ('[',']')] inner)
+ _ <- pMatchChar "}" (== '}')
+ let lo = concatMap snd gs
+ return (la, lo)
-pLocator :: LocatorMap -> Parsec [Inline] st (String, String)
-pLocator locMap = try $ do
- optional $ pMatch (== Str ",")
- optional pSpace
- la <- try (do ts <- many1 (notFollowedBy (pWordWithDigits True) >> anyToken)
- case M.lookup (trim (stringify ts)) locMap of
- Just l -> return l
- Nothing -> mzero)
- <|> (lookAhead pDigit >> return "page")
- g <- pWordWithDigits True
- gs <- many (pWordWithDigits False)
+pLocatorLabelDelimited :: LocatorMap -> Parsec [Inline] st (String, Bool)
+pLocatorLabelDelimited locMap
+ = pLocatorLabel' locMap lim <|> return ("page", True)
+ where
+ lim = stringify <$> anyToken
+
+pLocatorIntegrated :: LocatorMap -> Parsec [Inline] st (String, String)
+pLocatorIntegrated locMap = try $ do
+ (la, wasImplicit) <- pLocatorLabelIntegrated locMap
+ -- if we got the label implicitly, we have presupposed the first one is going
+ -- to have a digit, so guarantee that. You _can_ have p. (a) because you
+ -- specified it.
+ let modifier = if wasImplicit
+ then requireDigits
+ else requireRomansOrDigits
+ g <- try $ pLocatorWordIntegrated (not wasImplicit) >>= modifier
+ gs <- many (try $ pLocatorWordIntegrated False >>= modifier)
let lo = concat (g:gs)
return (la, lo)
+pLocatorLabelIntegrated :: LocatorMap -> Parsec [Inline] st (String, Bool)
+pLocatorLabelIntegrated locMap
+ = pLocatorLabel' locMap lim <|> (lookAhead digital >> return ("page", True))
+ where
+ lim = try $ pLocatorWordIntegrated True >>= requireRomansOrDigits
+ digital = try $ pLocatorWordIntegrated True >>= requireDigits
+
+pLocatorLabel' :: LocatorMap -> Parsec [Inline] st String -> Parsec [Inline] st (String, Bool)
+pLocatorLabel' locMap lim = go ""
+ where
+ -- grow the match string until we hit the end
+ -- trying to find the largest match for a label
+ go acc = try $ do
+ -- advance at least one token each time
+ -- the pathological case is "p.3"
+ t <- anyToken
+ ts <- manyTill anyToken (try $ lookAhead lim)
+ let s = acc ++ stringify (t:ts)
+ case M.lookup (trim s) locMap of
+ -- try to find a longer one, or return this one
+ Just l -> go s <|> return (l, False)
+ Nothing -> go s
+
+-- hard requirement for a locator to have some real digits in it
+requireDigits :: (Bool, String) -> Parsec [Inline] st String
+requireDigits (_, s) = if not (any isDigit s)
+ then fail "requireDigits"
+ else return s
+
+-- soft requirement for a sequence with some roman or arabic parts
+-- (a)(iv) -- because iv is roman
+-- 1(a) -- because 1 is an actual digit
+-- NOT: a, (a)-(b), hello, (some text in brackets)
+requireRomansOrDigits :: (Bool, String) -> Parsec [Inline] st String
+requireRomansOrDigits (d, s) = if not d
+ then fail "requireRomansOrDigits"
+ else return s
+
+pLocatorWordIntegrated :: Bool -> Parsec [Inline] st (Bool, String)
+pLocatorWordIntegrated isFirst = try $ do
+ punct <- if isFirst
+ then return ""
+ else stringify <$> option (Str "") pLocatorSep
+ sp <- option "" (pSpace >> return " ")
+ (dig, s) <- pBalancedBraces [('(',')'), ('[',']'), ('{','}')] pPageSeq
+ return (dig, punct ++ sp ++ s)
+
+-- we want to capture: 123, 123A, C22, XVII, 33-44, 22-33; 22-11
+-- 34(1), 34A(A), 34(1)(i)(i), (1)(a)
+-- [17], [17]-[18], '591 [84]'
+-- (because CSL cannot pull out individual pages/sections
+-- to wrap in braces on a per-style basis)
+pBalancedBraces :: [(Char, Char)] -> Parsec [Inline] st (Bool, String) -> Parsec [Inline] st (Bool, String)
+pBalancedBraces braces p = try $ do
+ ss <- many1 surround
+ return $ anyWereDigitLike ss
+ where
+ except = notFollowedBy pBraces >> p
+ -- outer and inner
+ surround = foldl (\a (open, close) -> sur open close except <|> a) except braces
+
+ isc c = stringify <$> pMatchChar [c] (== c)
+
+ sur c c' m = try $ do
+ (d, mid) <- between (isc c) (isc c') (option (False, "") m)
+ return (d, [c] ++ mid ++ [c'])
+
+ flattened = concatMap (\(o, c) -> [o, c]) braces
+ pBraces = pMatchChar "braces" (`elem` flattened)
+
+-- YES 1, 1.2, 1.2.3
+-- NO 1., 1.2. a.6
+-- can't use sepBy because we want to leave trailing .s
+pPageSeq :: Parsec [Inline] st (Bool, String)
+pPageSeq = oneDotTwo <|> withPeriod
+ where
+ oneDotTwo = do
+ u <- pPageUnit
+ us <- many withPeriod
+ return $ anyWereDigitLike (u:us)
+ withPeriod = try $ do
+ -- .2
+ p <- pMatchChar "." (== '.')
+ u <- try pPageUnit
+ return (fst u, stringify p ++ snd u)
+
+anyWereDigitLike :: [(Bool, String)] -> (Bool, String)
+anyWereDigitLike as = (any fst as, concatMap snd as)
+
+pPageUnit :: Parsec [Inline] st (Bool, String)
+pPageUnit = roman <|> plainUnit
+ where
+ -- roman is a 'digit'
+ roman = (True,) <$> pRoman
+ plainUnit = do
+ ts <- many1 (notFollowedBy pSpace >>
+ notFollowedBy pLocatorPunct >>
+ anyToken)
+ let s = stringify ts
+ -- otherwise look for actual digits or -s
+ return (any isDigit s, s)
+
pRoman :: Parsec [Inline] st String
pRoman = try $ do
t <- anyToken
@@ -477,37 +628,37 @@ pRoman = try $ do
Just _ -> return xs
_ -> mzero
--- we want to capture: 123, 123A, C22, XVII, 33-44, 22-33; 22-11
-pWordWithDigits :: Bool -> Parsec [Inline] st String
-pWordWithDigits isfirst = try $ do
- punct <- if isfirst
- then return ""
- else stringify `fmap` pLocatorPunct
- sp <- option "" (pSpace >> return " ")
- s <- pRoman <|>
- try (do ts <- many1 (notFollowedBy pSpace >>
- notFollowedBy pLocatorPunct >>
- anyToken)
- let ts' = stringify ts
- guard (any isDigit ts')
- return ts')
- return $ punct ++ sp ++ s
-
-pDigit :: Parsec [Inline] st ()
-pDigit = do
- t <- anyToken
- case t of
- Str (d:_) | isDigit d -> return ()
- _ -> mzero
+isLocatorPunct :: Char -> Bool
+isLocatorPunct '-' = False -- page range
+isLocatorPunct ':' = False -- vol:page-range hack
+isLocatorPunct c = isPunctuation c -- includes [{()}]
pLocatorPunct :: Parsec [Inline] st Inline
-pLocatorPunct = pMatch isLocatorPunct'
- where isLocatorPunct' (Str [c]) = isLocatorPunct c
- isLocatorPunct' _ = False
+pLocatorPunct = pMatchChar "punctuation" isLocatorPunct
-isLocatorPunct :: Char -> Bool
-isLocatorPunct ':' = False
-isLocatorPunct c = isPunctuation c
+pLocatorSep :: Parsec [Inline] st Inline
+pLocatorSep = pMatchChar "locator separator" isLocatorSep
+
+isLocatorSep :: Char -> Bool
+isLocatorSep ',' = True
+isLocatorSep ';' = True
+isLocatorSep _ = False
+
+pMatchChar :: String -> (Char -> Bool) -> Parsec [Inline] st Inline
+pMatchChar msg f = pMatch msg mc
+ where
+ mc (Str [c]) = f c
+ mc _ = False
+
+pSpace :: Parsec [Inline] st Inline
+pSpace = pMatch "' '" (\t -> isSpacy t || t == Str "\160")
+
+pMatch :: String -> (Inline -> Bool) -> Parsec [Inline] st Inline
+pMatch msg condition = try $ do
+ t <- anyToken
+ if not (condition t)
+ then fail msg
+ else return t
type LocatorMap = M.Map String String
diff --git a/src/Text/CSL/Proc/Disamb.hs b/src/Text/CSL/Proc/Disamb.hs
index 05a99f1..de8f17c 100644
--- a/src/Text/CSL/Proc/Disamb.hs
+++ b/src/Text/CSL/Proc/Disamb.hs
@@ -25,13 +25,13 @@ module Text.CSL.Proc.Disamb where
import Prelude
import Control.Arrow (second, (&&&), (>>>))
import Data.List (elemIndex, find, findIndex, groupBy,
- isPrefixOf, mapAccumL, nub, nubBy, sortBy)
+ isPrefixOf, mapAccumL, nub, nubBy, sortOn)
import Data.Maybe
-import Data.Ord (comparing)
import Text.CSL.Eval
import Text.CSL.Reference
import Text.CSL.Style
import Text.CSL.Util (proc, query)
+import Text.Pandoc.Shared (ordNub)
-- | Given the 'Style', the list of references and the citation
-- groups, disambiguate citations according to the style options.
@@ -42,7 +42,7 @@ disambCitations s bibs cs groups
where
-- utils
when_ b f = if b then f else []
- filter_ f = concatMap (map fst) . map (filter f) . map (uncurry zip)
+ filter_ f = concatMap (map fst . filter f . uncurry zip)
-- the list of the position and the reference of each citation
-- for each citation group.
@@ -89,14 +89,16 @@ disambCitations s bibs cs groups
withYearS = addNames $
if hasYSuffOpt
- then map (mapCitationGroup $ setYearSuffCollision hasNamesOpt needYSuff) $ reEvaluated
- else rmYearSuff $ reEvaluated
+ then map (mapCitationGroup
+ (setYearSuffCollision hasNamesOpt needYSuff))
+ reEvaluated
+ else rmYearSuff reEvaluated
yearSuffs = when_ hasYSuffOpt . generateYearSuffix bibs . concatMap getYearSuffixes $ withYearS
addNames = proc (updateContrib giveNameDisamb newNames newGName)
processed = if hasYSuffOpt
- then proc (updateYearSuffixes yearSuffs) $ withYearS
+ then proc (updateYearSuffixes yearSuffs) withYearS
else withYearS
citOutput = if disOpts /= [] then processed else reEvaluated
@@ -122,10 +124,12 @@ disambAddNames b needName = addLNames
needName' = nub' needName []
addLNames = map (\(c,n) -> c { disambed = if null n then collision c else head n }) disSolved
nub' [] r = r
- nub' (x:xs) r = case elemIndex (disambData $ clean x) (map (disambData . clean) r) of
+ nub' (x:xs) r = case elemIndex (disambData $ clean x)
+ (map (disambData . clean) r) of
Nothing -> nub' xs (x:r)
Just i -> let y = r !! i
- in nub' xs (y {sameAs = key x : sameAs y} : filter (/= y) r)
+ in nub' xs (y {sameAs = key x : sameAs y}
+ : filter (/= y) r)
disambAddGivenNames :: [NameData] -> [NameData]
disambAddGivenNames needName = addGName
@@ -158,9 +162,13 @@ updateOName n o
-- | Evaluate again a citation group with the 'EvalState' 'disamb'
-- field set to 'True' (for matching the @\"disambiguate\"@
-- condition).
-reEvaluate :: Style -> [CiteData] -> [(Cite, Maybe Reference)] -> CitationGroup -> CitationGroup
-reEvaluate (Style {citation = ct, csMacros = ms , styleLocale = lo,
- styleAbbrevs = as}) l cr (CG a f d os)
+reEvaluate :: Style
+ -> [CiteData]
+ -> [(Cite, Maybe Reference)]
+ -> CitationGroup
+ -> CitationGroup
+reEvaluate Style{citation = ct, csMacros = ms , styleLocale = lo,
+ styleAbbrevs = as} l cr (CG a f d os)
= CG a f d . flip concatMap (zip cr os) $
\((c,mbr),out) ->
case mbr of
@@ -175,8 +183,8 @@ reEvaluate (Style {citation = ct, csMacros = ms , styleLocale = lo,
-- disambiguation strategies have failed. To be used with the generic
-- 'query' function.
hasIfDis :: IfThen -> [Bool]
-hasIfDis (IfThen (Condition {disambiguation = (_:_)}) _ _) = [True]
-hasIfDis _ = [False]
+hasIfDis (IfThen Condition{disambiguation = (_:_)} _ _) = [True]
+hasIfDis _ = [False]
-- | Get the list of disambiguation options set in the 'Style' for
-- citations.
@@ -194,13 +202,15 @@ getCitDisambOptions
-- year-suffix option is set).
getDuplCiteData :: GiveNameDisambiguation -> Bool -> Bool -> [CitationGroup] -> [[CiteData]]
getDuplCiteData giveNameDisamb b1 b2 g
- = groupBy (\x y -> collide x == collide y) . sortBy (comparing collide)
+ = groupBy (\x y -> collide x == collide y) . sortOn collide
$ duplicates
where
whatToGet = if b1 then collision else disambYS
- collide = proc (rmExtras giveNameDisamb) . proc rmHashAndGivenNames . whatToGet
+ collide = proc (rmExtras giveNameDisamb) .
+ proc rmHashAndGivenNames .
+ whatToGet
citeData = nubBy (\a b -> collide a == collide b && key a == key b) $
- concatMap (mapGroupOutput $ getCiteData) g
+ concatMap (mapGroupOutput getCiteData) g
duplicates = [c | c <- citeData , d <- citeData , collides c d]
collides x y = x /= y && (collide x == collide y)
&& (not b2 || citYear x == citYear y)
@@ -252,15 +262,14 @@ getYears o
getDuplNameData :: [CitationGroup] -> [[NameData]]
getDuplNameData g
- = groupBy (\a b -> collide a == collide b) . sortBy (comparing collide) $ duplicates
+ = groupBy (\a b -> collide a == collide b) . sortOn collide $ duplicates
where
collide = nameCollision
nameData = nub $ concatMap (mapGroupOutput getName) g
duplicates = filter (flip elem (getDuplNames g) . collide) nameData
getDuplNames :: [CitationGroup] -> [[Output]]
-getDuplNames xs
- = nub . catMaybes . snd . mapAccumL dupl [] . getData $ xs
+getDuplNames = ordNub . catMaybes . snd . mapAccumL dupl [] . getData
where
getData = concatMap (mapGroupOutput getName)
dupl a c = if nameCollision c `elem` map nameCollision a
@@ -278,14 +287,14 @@ generateYearSuffix :: [Reference] -> [(String, [Output])] -> [(String,String)]
generateYearSuffix refs
= concatMap (`zip` suffs) .
-- sort clashing cites using their position in the sorted bibliography
- getFst . map (sort' . (filter ((/=) 0 . snd)) . (map getP)) .
+ getFst . map (sort' . filter ((/=) 0 . snd) . map getP) .
-- group clashing cites
getFst . filter (\grp -> length grp >= 2) . map nub .
groupBy (\a b -> snd a == snd b) .
sort' . filter (not . null . snd)
where
sort' :: (Ord a, Ord b) => [(a,b)] -> [(a,b)]
- sort' = sortBy (comparing snd)
+ sort' = sortOn snd
getFst = map $ map fst
getP k = case findIndex ((==) k . unLiteral . refId) refs of
Just x -> (k, x + 1)
@@ -294,16 +303,17 @@ generateYearSuffix refs
letters = map (:[]) ['a'..'z']
setYearSuffCollision :: Bool -> [CiteData] -> [Output] -> [Output]
-setYearSuffCollision b cs = proc (setYS cs) . (map $ \x -> if hasYearSuf x then x else addYearSuffix x)
+setYearSuffCollision b cs = proc (setYS cs) .
+ map (\x -> if hasYearSuf x then x else addYearSuffix x)
where
setYS c o
| OYearSuf _ k _ f <- o = OYearSuf [] k (getCollision k c) f
| otherwise = o
collide = if b then disambed else disambYS
getCollision k c = case find ((==) k . key) c of
- Just x -> if collide x == []
- then [OStr (citYear x) emptyFormatting]
- else collide x
+ Just x -> case collide x of
+ [] -> [OStr (citYear x) emptyFormatting]
+ ys -> ys
_ -> []
updateYearSuffixes :: [(String, String)] -> Output -> Output
@@ -334,8 +344,8 @@ rmYearSuff :: [CitationGroup] -> [CitationGroup]
rmYearSuff = proc rmYS
where
rmYS o
- | OYearSuf _ _ _ _ <- o = ONull
- | otherwise = o
+ | OYearSuf{} <- o = ONull
+ | otherwise = o
-- List Utilities
@@ -353,8 +363,7 @@ disambiguate l
heads = map (take 1) l
rest = map (\(b,x) -> if b then tail_ x else take 1 x) . zip (same heads)
- hasMult [] = False
- hasMult (x:xs) = length x > 1 || hasMult xs
+ hasMult = foldr (\x -> (||) (length x > 1)) False
tail_ [x] = [x]
tail_ x = if null x then x else tail x
@@ -396,16 +405,16 @@ addYearSuffix o
hasYear :: Output -> Bool
hasYear = not . null . query getYear
where getYear o
- | OYear _ _ _ <- o = [o]
- | otherwise = []
+ | OYear{} <- o = [o]
+ | otherwise = []
hasYearSuf :: Output -> Bool
hasYearSuf = not . null . query getYearSuf
where getYearSuf :: Output -> [String]
getYearSuf o
- | OYearSuf _ _ _ _ <- o = ["a"]
- | otherwise = []
+ | OYearSuf{} <- o = ["a"]
+ | otherwise = []
-- | Removes all given names and name hashes from OName elements.
rmHashAndGivenNames :: Output -> Output
@@ -430,4 +439,4 @@ addGivenNames
-- | Map the evaluated output of a citation group.
mapGroupOutput :: (Output -> [a]) -> CitationGroup -> [a]
-mapGroupOutput f (CG _ _ _ os) = concatMap f $ map snd os
+mapGroupOutput f (CG _ _ _ os) = concatMap (f . snd) os
diff --git a/src/Text/CSL/Reference.hs b/src/Text/CSL/Reference.hs
index 4924a59..4468a26 100644
--- a/src/Text/CSL/Reference.hs
+++ b/src/Text/CSL/Reference.hs
@@ -66,7 +66,7 @@ import Data.Char (isDigit, toLower)
import Data.Either (lefts, rights)
import Data.Generics hiding (Generic)
import qualified Data.HashMap.Strict as H
-import Data.List (elemIndex)
+import Data.List (findIndex, elemIndex)
import Data.List.Split (splitWhen)
import Data.Maybe (fromMaybe, isNothing)
import Data.String
@@ -408,6 +408,7 @@ instance ToYaml CLabel where
data Reference =
Reference
{ refId :: Literal
+ , refOtherIds :: [Literal]
, refType :: RefType
, author :: [Agent]
@@ -493,6 +494,7 @@ instance FromJSON Reference where
v <- parseSuppFields v' <|> return v'
addPageFirst <$> (Reference <$>
v .:? "id" .!= "" <*>
+ v .:? "other-ids" .!= [] <*>
v .:? "type" .!= NoType <*>
v .:? "author" .!= [] <*>
v .:? "editor" .!= [] <*>
@@ -620,6 +622,7 @@ regText = (T.pack <$> P.many1 (P.noneOf "\n{")) <|> (T.singleton <$> P.anyChar)
instance ToJSON Reference where
toJSON ref = object' [
"id" .= refId ref
+ , "other-ids" .= refOtherIds ref
, "type" .= refType ref
, "author" .= author ref
, "editor" .= editor ref
@@ -699,6 +702,7 @@ instance ToJSON Reference where
instance ToYaml Reference where
toYaml ref = mapping' [
"id" &= refId ref
+ , "other-ids" &= refOtherIds ref
, (("type" Y..= refType ref) :)
, "author" &= author ref
, "editor" &= editor ref
@@ -786,6 +790,7 @@ emptyReference :: Reference
emptyReference =
Reference
{ refId = mempty
+ , refOtherIds = mempty
, refType = NoType
, author = []
@@ -871,43 +876,81 @@ numericVars = [ "edition", "volume", "number-of-volumes", "number", "issue", "ci
, "chapter-number", "collection-number", "number-of-pages"]
getReference :: [Reference] -> Cite -> Maybe Reference
-getReference r c
- = case citeId c `elemIndex` map (unLiteral . refId) r of
- Just i -> Just $ setPageFirst $ r !! i
+getReference rs c
+ = case (hasId (citeId c)) `findIndex` rs of
+ Just i -> Just $ setPageFirst $ rs !! i
Nothing -> Nothing
+ where hasId :: String -> Reference -> Bool
+ hasId ident r = ident `elem` (map unLiteral (refId r : refOtherIds r))
processCites :: [Reference] -> [[Cite]] -> [[(Cite, Maybe Reference)]]
processCites rs cs
- = procGr [[]] cs
+ = procGr [] cs
where
procRef r = case filter ((==) (unLiteral $ refId r) . citeId) $ concat cs of
x:_ -> r { firstReferenceNoteNumber = readNum $ citeNoteNumber x}
[] -> r
procGr _ [] = []
- procGr a (x:xs) = let (a',res) = procCs a x
- in res : procGr (a' ++ [[]]) xs
-
- procCs a [] = (a,[])
- procCs a (c:xs)
- | isIbid, isLocSet = go "ibid-with-locator"
- | isIbid = go "ibid"
- | isElem = go "subsequent"
- | otherwise = go "first"
+ procGr acc (x:xs) = let (a',res) = procCs acc x
+ in res : procGr ([] : a') xs
+
+ -- process, given the accumulated history, the current group's cites
+ procCs acc [] = (acc,[])
+ procCs acc (c:xs) = let (a, rest) = procCs addCite xs
+ ref = procRef <$> getReference rs c
+ c' = c { citePosition = getCitePosition }
+ in (a, (c', ref) : rest)
where
- go s = let addCite = init a ++ [last a ++ [c]]
- (a', rest) = procCs addCite xs
- in (a', (c { citePosition = s},
- procRef <$> getReference rs c) : rest)
- isElem = citeId c `elem` map citeId (concat a)
- isIbid = case reverse (last a) of
- [] -> case reverse (init a) of
- [] -> False
- (zs:_) -> not (null zs) &&
- all (== citeId c)
- (map citeId zs)
- (x:_) -> citeId c == citeId x
- isLocSet = citeLocator c /= ""
+ addCite = case acc of
+ [] -> [[c]]
+ (a:as) -> (c : a) : as
+
+ -- http://docs.citationstyles.org/en/stable/specification.html#locators
+ getCitePosition = fromMaybe notIbid (ibidPosition <$> prevSameCite)
+ where
+ notIbid = if citeId c `elem` map citeId (concat acc)
+ then "subsequent"
+ else "first"
+
+ ibidPosition x = let hasL k = citeLocator k /= ""
+ withIf b = if b then "ibid-with-locator" else "ibid"
+ diffLoc = citeLocator x /= citeLocator c
+ || citeLabel x /= citeLabel c
+ in case (hasL x, hasL c) of
+ (False, cur) -> withIf cur
+ (True, True) -> withIf diffLoc
+ (True, False) -> "subsequent"
+
+ -- x is previous cite in current group
+ -- zs is the previous group
+ prevSameCite = case acc of
+ [] -> Nothing
+ (a:as) -> psc a as
+ where
+ -- you can't have an ibid at the start of your document
+ psc [] [] = Nothing
+
+ -- a. the current cite immediately follows on another cite,
+ -- within the same citation, that references the same item
+ psc (x:_) _ = if citeId c == citeId x
+ then Just x
+ else Nothing
+
+ -- b. [] => the current cite is the first cite in the citation
+ -- zs => and the previous citation consists of a single cite
+ -- that refs the same item
+ -- The spec appears to be concerned that you cannot know the
+ -- correct one to match the locator against.
+ -- It is a super clunky if you have [@a, 1; @a, 2] then [@a, 3],
+ -- where the second citation gets "subsequent" even though there were
+ -- only @a keys.
+ -- This is silly. We will use the last one to match against.
+ psc [] (zs:_) = case zs of
+ [] -> Nothing
+ (z:_) -> if all (== citeId c) (map citeId zs)
+ then Just z
+ else Nothing
setPageFirst :: Reference -> Reference
setPageFirst ref =
@@ -1039,24 +1082,24 @@ rawDateOld = do
let sep = P.oneOf [' ','/',','] >> P.spaces
let rangesep = P.try $ P.spaces >> P.char '-' >> P.spaces
let refDate = RefDate Nothing Nothing Nothing Nothing mempty False
- let date = P.choice $ map P.try [
- (do s <- pseason
+ let date = P.choice $ map P.try
+ [ do s <- pseason
sep
y <- pyear
- return refDate{ year = y, season = s })
- , (do m <- pmonth
+ return refDate{ year = y, season = s }
+ , do m <- pmonth
sep
d <- pday
sep
y <- pyear
- return refDate{ year = y, month = m, day = d })
- , (do m <- pmonth
+ return refDate{ year = y, month = m, day = d }
+ , do m <- pmonth
sep
y <- pyear
- return refDate{ year = y, month = m })
- , (do y <- pyear
- return refDate{ year = y })
- ]
+ return refDate{ year = y, month = m }
+ , do y <- pyear
+ return refDate{ year = y }
+ ]
d1 <- date
P.option [d1] ((\x -> [d1,x]) <$> (rangesep >> date))
diff --git a/stack.yaml b/stack.yaml
index 588b4b3..3a25c56 100644
--- a/stack.yaml
+++ b/stack.yaml
@@ -9,16 +9,19 @@ flags:
packages:
- '.'
extra-deps:
-- haddock-library-1.6.0
-- HsYAML-0.1.1.1
-- network-2.7.0.2
- rfc5051-0.1.0.3
-- hs-bibutils-6.5.0.0
-- yaml-0.9.0
-- hslua-1.0.0
+- hs-bibutils-6.6.0.0
+- pandoc-2.4
+- haddock-library-1.7.0
+- HsYAML-0.1.1.2
+- texmath-0.11.1.2
+- yaml-0.11.0.0
+- libyaml-0.1.0.0
+- cmark-gfm-0.1.6
+- hslua-1.0.1
- hslua-module-text-0.2.0
-- github: jgm/pandoc
- commit: c292a0bae3167570511543d3d96a0385b9b5f2d8
+- skylighting-0.7.4
+- skylighting-core-0.7.4
ghc-options:
"$locals": -fhide-source-paths
-resolver: lts-12.0
+resolver: lts-12.18
diff --git a/tests/biblio2yaml/266.biblatex b/tests/biblio2yaml/266.biblatex
new file mode 100644
index 0000000..30bc08f
--- /dev/null
+++ b/tests/biblio2yaml/266.biblatex
@@ -0,0 +1,28 @@
+@book{goethe2005,
+ langid = {german},
+ location = {{Frankfurt am Main}},
+ title = {Not A Real Book},
+ date = {2005},
+ author = {family=Goethe, given=Johann Wolfgang, prefix=von, useprefix=false and given=Antonie, prefix=van, family=Leeuwenhoek, useprefix=true}, editor = {Schöne, Albrecht}
+}
+
+---
+references:
+- id: goethe2005
+ type: book
+ author:
+ - family: Goethe
+ given: Johann Wolfgang
+ dropping-particle: von
+ - family: Leeuwenhoek
+ given: Antonie
+ non-dropping-particle: van
+ editor:
+ - family: Schöne
+ given: Albrecht
+ issued:
+ - year: 2005
+ title: Not A Real Book
+ publisher-place: Frankfurt am Main
+ language: de-DE
+...
diff --git a/tests/issue356.expected.native b/tests/issue356.expected.native
new file mode 100644
index 0000000..d98b5f0
--- /dev/null
+++ b/tests/issue356.expected.native
@@ -0,0 +1,5 @@
+Pandoc (Meta {unMeta = fromList [("references",MetaList [MetaMap (fromList [("author",MetaList [MetaMap (fromList [("family",MetaInlines [Str "Alice"])])]),("id",MetaInlines [Str "foo"]),("issued",MetaList [MetaMap (fromList [("year",MetaInlines [Str "2042"])])]),("other-ids",MetaList [MetaInlines [Str "bar"],MetaInlines [Str "doz"]]),("type",MetaInlines [Str "book"])])])]})
+[Para [Cite [Citation {citationId = "bar", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 1}] [Str "(Alice",Space,Str "2042)"]]
+,Div ("refs",["references"],[])
+ [Div ("ref-foo",[],[])
+ [Para [Str "Alice.",Space,Str "2042."]]]]
diff --git a/tests/issue356.in.native b/tests/issue356.in.native
new file mode 100644
index 0000000..fc5f8d7
--- /dev/null
+++ b/tests/issue356.in.native
@@ -0,0 +1,2 @@
+Pandoc (Meta {unMeta = fromList [("references",MetaList [MetaMap (fromList [("author",MetaList [MetaMap (fromList [("family",MetaInlines [Str "Alice"])])]),("id",MetaInlines [Str "foo"]),("issued",MetaList [MetaMap (fromList [("year",MetaInlines [Str "2042"])])]),("other-ids",MetaList [MetaInlines [Str "bar"],MetaInlines [Str "doz"]]),("type",MetaInlines [Str "book"])])])]})
+[Para [Cite [Citation {citationId = "bar", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@bar]"]]]
diff --git a/tests/issue361.expected.native b/tests/issue361.expected.native
new file mode 100644
index 0000000..e941b85
--- /dev/null
+++ b/tests/issue361.expected.native
@@ -0,0 +1,12 @@
+Pandoc (Meta {unMeta = fromList [("csl",MetaInlines [Str "tests/locators.csl"]),("references",MetaList [MetaMap (fromList [("id",MetaInlines [Str "citekey"]),("title",MetaInlines [Str "Title"]),("type",MetaInlines [Str "article-journal"])]),MetaMap (fromList [("id",MetaInlines [Str "other"]),("title",MetaInlines [Str "Other"]),("type",MetaInlines [Str "article-journal"])])]),("suppress-bibliography",MetaBool True)]})
+[Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 1}] [Note [Para [Str "Title."]]]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "1"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 2}] [Note [Para [Str "Ibid-with-locator",Space,Str "{1}."]]]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "2"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 3}] [Note [Para [Str "Ibid-with-locator",Space,Str "{2}."]]]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "2"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 4}] [Note [Para [Str "Ibid."]]]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Space,Str "2"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 5}] [Note [Para [Str "Ibid."]]]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.\160\&2"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 6}] [Note [Para [Str "Ibid."]]]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Space,Str "p.\160\&2"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 7}] [Note [Para [Str "Ibid."]]]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 8}] [Note [Para [Str "Subsequent."]]]]
+,Para [Str "Content",Cite [Citation {citationId = "other", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 9}] [Note [Para [Str "Other."]]]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "3"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 10}] [Note [Para [Str "Subsequent",Space,Str "{3}."]]]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "3"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 11}] [Note [Para [Str "Ibid."]]]]]
diff --git a/tests/issue361.in.native b/tests/issue361.in.native
new file mode 100644
index 0000000..f945018
--- /dev/null
+++ b/tests/issue361.in.native
@@ -0,0 +1,12 @@
+Pandoc (Meta {unMeta = fromList [("csl",MetaInlines [Str "tests/locators.csl"]),("references",MetaList [MetaMap (fromList [("id",MetaInlines [Str "citekey"]),("title",MetaInlines [Str "Title"]),("type",MetaInlines [Str "article-journal"])]),MetaMap (fromList [("id",MetaInlines [Str "other"]),("title",MetaInlines [Str "Other"]),("type",MetaInlines [Str "article-journal"])])]),("suppress-bibliography",MetaBool True)]})
+[Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey]"]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "1"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "1]"]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "2"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "2]"]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "2"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "2]"]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Space,Str "2"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey",Space,Str "2]"]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.\160\&2"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "p.",Space,Str "2]"]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Space,Str "p.\160\&2"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey",Space,Str "p.",Space,Str "2]"]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey]"]]
+,Para [Str "Content",Cite [Citation {citationId = "other", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@other]"]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "3"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "3]"]]
+,Para [Str "Content",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "3"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "3]"]]]
diff --git a/tests/issue365.expected.native b/tests/issue365.expected.native
new file mode 100644
index 0000000..c80e70c
--- /dev/null
+++ b/tests/issue365.expected.native
@@ -0,0 +1,5 @@
+Pandoc (Meta {unMeta = fromList [("csl",MetaInlines [Str "tests/le-tapuscrit-note.csl"]),("references",MetaList [MetaMap (fromList [("ISBN",MetaInlines [Str "978-2-912573-52-0"]),("author",MetaList [MetaMap (fromList [("family",MetaInlines [Str "Le\160Gras"]),("given",MetaInlines [Str "Gw\233na\235lle"])])]),("call-number",MetaInlines [Str "Tolbiac",Space,Str "-",Space,Str "Rez",Space,Str "de",Space,Str "Jardin",Space,Str "-",Space,Str "Litt\233rature",Space,Str "et",Space,Str "art",Space,Str "-",Space,Str "Magasin",Space,Str "-",Space,Str "2010-82178"]),("collection-title",MetaInlines [Str "Jeux",Space,Str "d\8217acteurs"]),("id",MetaInlines [Str "legras_michel_2010"]),("issued",MetaList [MetaMap (fromList [("year",MetaInlines [Str "2010"])])]),("language",MetaInlines [Str "fre"]),("number-of-pages",MetaInlines [Str "128"]),("publisher",MetaInlines [Str "Scope"]),("publisher-place",MetaInlines [Str "Paris"]),("source",MetaInlines [Str "BnF",Space,Str "Catalogue",Space,Str "g\233n\233ral",Space,Str "(http://catalogue.bnf.fr)"]),("title",MetaInlines [Str "Michel",Space,Str "Simon\160:",Space,Str "l\8217art",Space,Str "de",Space,Str "la",Space,Str "disgr\226ce"]),("title-short",MetaInlines [Str "Michel",Space,Str "Simon"]),("type",MetaInlines [Str "book"])])])]})
+[Para [Str "Foo",Str ".",Cite [Citation {citationId = "legras_michel_2010", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 1}] [Note [Para [Str "Gw\233na\235lle",Space,Str "Le\160Gras,",Space,Emph [Str "Michel",Space,Str "Simon\160:",Space,Str "l\8217art",Space,Str "de",Space,Str "la",Space,Str "disgr\226ce"],Str ",",Space,Str "Paris,",Space,Str "Scope,",Space,Str "2010,",Space,Str "128\160p."]]]]
+,Div ("refs",["references"],[])
+ [Div ("ref-legras_michel_2010",[],[])
+ [Para [SmallCaps [Str "Le\160Gras"],Space,Str "Gw\233na\235lle,",Space,Emph [Str "Michel",Space,Str "Simon\160:",Space,Str "l\8217art",Space,Str "de",Space,Str "la",Space,Str "disgr\226ce"],Str ",",Space,Str "Paris,",Space,Str "Scope",Space,Str "(coll.\160\171\160Jeux",Space,Str "d\8217acteurs\160\187),",Space,Str "2010,",Space,Str "128\160p."]]]]
diff --git a/tests/issue365.in.native b/tests/issue365.in.native
new file mode 100644
index 0000000..4c4c824
--- /dev/null
+++ b/tests/issue365.in.native
@@ -0,0 +1,2 @@
+Pandoc (Meta {unMeta = fromList [("csl",MetaInlines [Str "tests/le-tapuscrit-note.csl"]),("references",MetaList [MetaMap (fromList [("ISBN",MetaInlines [Str "978-2-912573-52-0"]),("author",MetaList [MetaMap (fromList [("family",MetaInlines [Str "Le\160Gras"]),("given",MetaInlines [Str "Gw\233na\235lle"])])]),("call-number",MetaInlines [Str "Tolbiac",Space,Str "-",Space,Str "Rez",Space,Str "de",Space,Str "Jardin",Space,Str "-",Space,Str "Litt\233rature",Space,Str "et",Space,Str "art",Space,Str "-",Space,Str "Magasin",Space,Str "-",Space,Str "2010-82178"]),("collection-title",MetaInlines [Str "Jeux",Space,Str "d\8217acteurs"]),("id",MetaInlines [Str "legras_michel_2010"]),("issued",MetaList [MetaMap (fromList [("year",MetaInlines [Str "2010"])])]),("language",MetaInlines [Str "fre"]),("number-of-pages",MetaInlines [Str "128"]),("publisher",MetaInlines [Str "Scope"]),("publisher-place",MetaInlines [Str "Paris"]),("source",MetaInlines [Str "BnF",Space,Str "Catalogue",Space,Str "g\233n\233ral",Space,Str "(http://catalogue.bnf.fr)"]),("title",MetaInlines [Str "Michel",Space,Str "Simon\160:",Space,Str "l\8217art",Space,Str "de",Space,Str "la",Space,Str "disgr\226ce"]),("title-short",MetaInlines [Str "Michel",Space,Str "Simon"]),("type",MetaInlines [Str "book"])])])]})
+[Para [Str "Foo",Space,Cite [Citation {citationId = "legras_michel_2010", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@legras_michel_2010]"],Str "."]]
diff --git a/tests/le-tapuscrit-note.csl b/tests/le-tapuscrit-note.csl
new file mode 100644
index 0000000..b0680b4
--- /dev/null
+++ b/tests/le-tapuscrit-note.csl
@@ -0,0 +1,496 @@
+<?xml version="1.0" encoding="utf-8"?>
+<style xmlns="http://purl.org/net/xbiblio/csl" class="note" default-locale="fr-FR" version="1.0" page-range-format="expanded">
+ <info>
+ <title>Le tapuscrit (École des hautes études en sciences sociales) (note, French)</title>
+ <title-short>Tapuscrit-EHESS</title-short>
+ <id>http://www.zotero.org/styles/le-tapuscrit-note</id>
+ <link href="http://www.zotero.org/styles/le-tapuscrit-note" rel="self"/>
+ <link href="http://www.editions.ehess.fr/ouvrages/ouvrage/le-tapuscrit/" rel="documentation"/>
+ <author>
+ <name>Franziska Heimburger</name>
+ <email>zotero@franziska.fr</email>
+ </author>
+ <category citation-format="note"/>
+ <category field="social_science"/>
+ <category field="generic-base"/>
+ <updated>2018-07-12T11:20:37+00:00</updated>
+ <rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
+ </info>
+ <locale xml:lang="fr">
+ <terms>
+ <term name="ordinal-01">ère</term>
+ <term name="ordinal-02">e</term>
+ <term name="ordinal-03">e</term>
+ <term name="ordinal-04">e</term>
+ <term name="cited">op.&#160;cit.</term>
+ <term name="page" form="short">p.</term>
+ <term name="editor" form="short">
+ <single>ed.</single>
+ <multiple>eds.</multiple>
+ </term>
+ <term name="in">dans</term>
+ </terms>
+ </locale>
+ <macro name="author">
+ <choose>
+ <if variable="author">
+ <names variable="author">
+ <name form="long" and="text" delimiter-precedes-last="never" sort-separator=" "/>
+ </names>
+ </if>
+ <else-if variable="editor">
+ <names variable="editor">
+ <name form="long" and="text" delimiter-precedes-last="never" sort-separator=" "/>
+ <label form="short" prefix="&#160;(" suffix=".)"/>
+ </names>
+ </else-if>
+ </choose>
+ </macro>
+ <macro name="author-bib">
+ <choose>
+ <if variable="author">
+ <names variable="author">
+ <name name-as-sort-order="all" form="long" and="text" delimiter-precedes-last="never" sort-separator=" ">
+ <name-part name="family" font-variant="small-caps"/>
+ </name>
+ </names>
+ </if>
+ <else-if variable="editor">
+ <names variable="editor">
+ <name name-as-sort-order="all" form="long" and="text" delimiter-precedes-last="never" sort-separator=" ">
+ <name-part name="family" font-variant="small-caps"/>
+ </name>
+ <label form="short" prefix="&#160;(" suffix=".)"/>
+ </names>
+ </else-if>
+ </choose>
+ </macro>
+ <macro name="author-ibid">
+ <choose>
+ <if variable="author">
+ <names variable="author">
+ <name and="text" initialize="true" initialize-with="." delimiter-precedes-last="never" sort-separator=" " font-style="normal"/>
+ </names>
+ </if>
+ <else-if variable="editor">
+ <names variable="editor">
+ <name form="long" and="text" delimiter-precedes-last="never" sort-separator=" "/>
+ <label form="short" prefix="&#160;(" suffix=".)"/>
+ </names>
+ </else-if>
+ </choose>
+ </macro>
+ <macro name="editor">
+ <names variable="editor">
+ <name form="long" and="text" delimiter-precedes-last="never" sort-separator=" "/>
+ <label form="short" prefix="&#160;(" suffix=".)"/>
+ </names>
+ </macro>
+ <macro name="translator">
+ <names variable="translator">
+ <name form="long" and="text" delimiter-precedes-last="never" sort-separator=" " prefix=" traduit par "/>
+ </names>
+ </macro>
+ <macro name="title">
+ <choose>
+ <if type="bill book graphic legal_case motion_picture report song" match="any">
+ <text variable="title" text-case="capitalize-first" font-style="italic"/>
+ </if>
+ <else-if type="article-journal article-newspaper article-magazine" match="any">
+ <group delimiter=", ">
+ <text variable="title" text-case="capitalize-first" quotes="true"/>
+ <text variable="container-title" font-style="italic"/>
+ </group>
+ </else-if>
+ <else-if type="thesis" match="any">
+ <group delimiter="">
+ <text variable="title" text-case="capitalize-first" font-style="italic" suffix=","/>
+ <text variable="genre" suffix=", " prefix=" "/>
+ <text variable="publisher"/>
+ </group>
+ </else-if>
+ <else-if type="manuscript" match="any">
+ <group delimiter=",">
+ <text variable="title" text-case="capitalize-first" font-style="italic"/>
+ <text variable="genre" prefix=" [" suffix="]"/>
+ </group>
+ </else-if>
+ <else-if type="chapter entry-dictionary entry-encyclopedia" match="any">
+ <group delimiter="">
+ <text variable="title" text-case="capitalize-first" quotes="true"/>
+ <text value="dans" suffix=" " prefix=" "/>
+ <text macro="editor" suffix=", "/>
+ <text variable="container-title" text-case="capitalize-first" font-style="italic"/>
+ </group>
+ </else-if>
+ <else-if type="webpage post-weblog" match="any">
+ <group delimiter="">
+ <text variable="title" text-case="capitalize-first" font-style="italic" suffix=", "/>
+ <text variable="URL"/>
+ <group prefix=" , ">
+ <date variable="issued">
+ <date-part name="day" suffix=" "/>
+ <date-part name="month" suffix=" "/>
+ <date-part name="year"/>
+ </date>
+ </group>
+ </group>
+ </else-if>
+ <else>
+ <text variable="title" quotes="true"/>
+ </else>
+ </choose>
+ </macro>
+ <macro name="pub-place">
+ <choose>
+ <if type="bill book chapter entry-dictionary entry-encyclopedia thesis graphic legal_case manuscript motion_picture paper-conference report song" match="any">
+ <choose>
+ <if variable="publisher-place" match="any">
+ <text variable="publisher-place"/>
+ </if>
+ <else>
+ <text value="s.l."/>
+ </else>
+ </choose>
+ </if>
+ </choose>
+ </macro>
+ <macro name="publisher">
+ <choose>
+ <if type="bill book chapter entry-dictionary entry-encyclopedia graphic legal_case motion_picture paper-conference report song" match="any">
+ <text variable="publisher"/>
+ </if>
+ </choose>
+ </macro>
+ <macro name="yearpage">
+ <choose>
+ <if type="bill book graphic legal_case motion_picture paper-conference manuscript report song thesis" match="any">
+ <group delimiter=", ">
+ <date variable="issued">
+ <date-part name="year"/>
+ </date>
+ <group>
+ <text term="volume" form="short" suffix="."/>
+ <text variable="number-of-volumes" prefix=". " suffix="/"/>
+ <text variable="volume"/>
+ </group>
+ <choose>
+ <if variable="locator" match="any">
+ <group delimiter="&#8239;">
+ <label variable="locator" form="short"/>
+ <text variable="locator"/>
+ </group>
+ </if>
+ <else-if variable="locator" match="none">
+ <text variable="number-of-pages" suffix="&#160;p"/>
+ </else-if>
+ </choose>
+ </group>
+ </if>
+ <else-if type="chapter entry-dictionary entry-encyclopedia" match="any">
+ <group delimiter=" ">
+ <date variable="issued">
+ <date-part name="year" suffix=", "/>
+ </date>
+ <group>
+ <text term="volume" form="short" suffix="."/>
+ <text variable="number-of-volumes" prefix=". " suffix="/"/>
+ <text variable="volume" suffix=","/>
+ </group>
+ <choose>
+ <if variable="locator" match="any">
+ <group delimiter="&#8239;">
+ <label variable="locator" form="short"/>
+ <text variable="locator"/>
+ </group>
+ </if>
+ <else-if variable="locator" match="none">
+ <label variable="page" form="short"/>
+ <text variable="page"/>
+ </else-if>
+ </choose>
+ </group>
+ </else-if>
+ <else-if type="article-journal" match="any">
+ <group delimiter=" " font-style="normal">
+ <choose>
+ <if variable="locator" match="any">
+ <group delimiter="&#8239;">
+ <label variable="locator" form="short"/>
+ <text variable="locator"/>
+ </group>
+ </if>
+ <else-if variable="locator" match="none">
+ <label variable="page" form="short"/>
+ <text variable="page"/>
+ </else-if>
+ </choose>
+ </group>
+ </else-if>
+ <else-if type="article-newspaper article-magazine" match="any">
+ <date variable="issued">
+ <date-part name="day" suffix=" "/>
+ <date-part name="month" form="short" suffix=" "/>
+ <date-part name="year"/>
+ </date>
+ <group delimiter=" " font-style="normal">
+ <label variable="page" form="short"/>
+ <text variable="page"/>
+ </group>
+ <group delimiter=" " font-style="normal">
+ <choose>
+ <if variable="locator" match="any">
+ <group delimiter="&#8239;">
+ <label variable="locator" form="short"/>
+ <text variable="locator"/>
+ </group>
+ </if>
+ <else-if variable="locator" match="none">
+ <label variable="page" form="short"/>
+ </else-if>
+ </choose>
+ </group>
+ </else-if>
+ <else-if type="webpage post-weblog" match="any">
+ <group delimiter=" " prefix="(" suffix=")">
+ <text value="consulté le" suffix=" " prefix=" "/>
+ <date variable="accessed" form="text">
+ <date-part name="day"/>
+ <date-part name="month"/>
+ <date-part name="year"/>
+ </date>
+ </group>
+ </else-if>
+ </choose>
+ </macro>
+ <macro name="yearpage-bib">
+ <choose>
+ <if type="bill book graphic legal_case motion_picture paper-conference report song thesis" match="any">
+ <group delimiter=", ">
+ <group delimiter=", ">
+ <date variable="issued">
+ <date-part name="year"/>
+ </date>
+ <group>
+ <text term="volume" form="short" suffix="."/>
+ <text variable="number-of-volumes" prefix=". " suffix="/"/>
+ <text variable="volume"/>
+ </group>
+ <text variable="number-of-pages" suffix="&#160;p"/>
+ </group>
+ <group>
+ <label variable="locator" form="short"/>
+ <text variable="locator"/>
+ </group>
+ </group>
+ </if>
+ <else-if type="chapter entry-dictionary entry-encyclopedia" match="any">
+ <group delimiter=", ">
+ <date variable="issued">
+ <date-part name="year"/>
+ </date>
+ <group>
+ <text term="volume" form="short" suffix="."/>
+ <text variable="number-of-volumes" prefix=". " suffix="/"/>
+ <text variable="volume"/>
+ </group>
+ <group>
+ <label variable="page" form="short"/>
+ <text variable="page" prefix="&#160;"/>
+ </group>
+ </group>
+ </else-if>
+ <else-if type="article-journal chapter" match="any">
+ <group delimiter=" ">
+ <label variable="page" form="short"/>
+ <text variable="page"/>
+ </group>
+ </else-if>
+ <else-if type="article-newspaper article-magazine" match="any">
+ <group delimiter=" ">
+ <date variable="issued">
+ <date-part name="day" suffix=" "/>
+ <date-part name="month" form="short" suffix=" "/>
+ <date-part name="year"/>
+ </date>
+ <label variable="page" form="short"/>
+ <text variable="page"/>
+ </group>
+ </else-if>
+ <else-if type="manuscript">
+ <group delimiter="" font-style="normal">
+ <choose>
+ <if variable="issued">
+ <date variable="issued">
+ <date-part name="day" suffix=" "/>
+ <date-part name="month" suffix=" "/>
+ <date-part name="year"/>
+ </date>
+ </if>
+ <else>
+ <text value="s. d."/>
+ </else>
+ </choose>
+ </group>
+ </else-if>
+ <else-if type="webpage post-weblog" match="any">
+ <group delimiter=" ">
+ <text value="consulté le" suffix=" " prefix=" "/>
+ <date variable="accessed" form="text">
+ <date-part name="day"/>
+ <date-part name="month"/>
+ <date-part name="year"/>
+ </date>
+ </group>
+ </else-if>
+ </choose>
+ </macro>
+ <macro name="edition">
+ <choose>
+ <if type="bill book graphic legal_case motion_picture report song chapter paper-conference" match="any">
+ <choose>
+ <if is-numeric="edition">
+ <group delimiter=" ">
+ <number variable="edition" form="ordinal"/>
+ <text term="edition" form="short"/>
+ </group>
+ </if>
+ <else>
+ <text variable="edition" text-case="capitalize-first" suffix="."/>
+ </else>
+ </choose>
+ </if>
+ <else-if type="article-journal article-magazine" match="any">
+ <group delimiter="">
+ <choose>
+ <if variable="issued">
+ <date variable="issued">
+ <date-part name="day" suffix=" "/>
+ <date-part name="month" suffix=" "/>
+ <date-part name="year"/>
+ </date>
+ <text macro="volume" prefix=", "/>
+ </if>
+ <else>
+ <text macro="volume" text-case="capitalize-first"/>
+ </else>
+ </choose>
+ </group>
+ </else-if>
+ </choose>
+ <text macro="issue" prefix=", "/>
+ </macro>
+ <macro name="volume">
+ <choose>
+ <if is-numeric="volume">
+ <text term="volume" form="short" suffix=".&#160;"/>
+ <text variable="volume"/>
+ </if>
+ <else>
+ <text variable="volume"/>
+ </else>
+ </choose>
+ </macro>
+ <macro name="issue">
+ <choose>
+ <if is-numeric="issue">
+ <text term="issue" form="short" suffix="&#160;"/>
+ <text variable="issue"/>
+ </if>
+ <else>
+ <text variable="issue"/>
+ </else>
+ </choose>
+ </macro>
+ <macro name="collection">
+ <text variable="collection-title" quotes="true" prefix=" (coll.&#160;" suffix=")"/>
+ </macro>
+ <citation et-al-min="4" et-al-use-first="1">
+ <layout suffix="." delimiter="&#160;; ">
+ <choose>
+ <if position="ibid-with-locator">
+ <group delimiter=", ">
+ <text term="ibid" text-case="capitalize-first" font-style="italic" suffix="."/>
+ <text variable="locator" prefix="p.&#160;"/>
+ </group>
+ </if>
+ <else-if position="ibid">
+ <text term="ibid" text-case="capitalize-first" font-style="italic"/>
+ </else-if>
+ <else-if position="subsequent">
+ <group delimiter=", ">
+ <text macro="author-ibid"/>
+ <choose>
+ <if type="bill book graphic legal_case motion_picture report song thesis manuscript" match="any">
+ <text variable="title" form="short" font-style="italic"/>
+ <text term="cited" font-style="italic" suffix="."/>
+ </if>
+ <else>
+ <text variable="title" text-case="capitalize-first" form="short" quotes="true"/>
+ <text value="art cit"/>
+ </else>
+ </choose>
+ <text variable="locator" prefix="p.&#160;"/>
+ </group>
+ </else-if>
+ <else>
+ <choose>
+ <if type="manuscript">
+ <group delimiter=", ">
+ <text variable="archive"/>
+ <text variable="archive_location"/>
+ <text variable="call-number"/>
+ <text macro="title"/>
+ <text macro="yearpage-bib"/>
+ </group>
+ </if>
+ <else-if type="bill chapter article-journal article-newspaper interview book graphic legal_case motion_picture paper-conference report song thesis webpage post-weblog article-magazine" match="any">
+ <group delimiter=", ">
+ <text macro="author"/>
+ <text macro="title"/>
+ <text macro="translator"/>
+ <text macro="edition"/>
+ <text macro="pub-place"/>
+ <text macro="publisher"/>
+ <text macro="yearpage"/>
+ </group>
+ </else-if>
+ </choose>
+ </else>
+ </choose>
+ </layout>
+ </citation>
+ <bibliography>
+ <sort>
+ <key macro="author" names-min="3" names-use-first="3"/>
+ <key variable="issued" sort="descending"/>
+ </sort>
+ <layout suffix=".">
+ <choose>
+ <if type="manuscript">
+ <group delimiter=", ">
+ <text variable="archive"/>
+ <text variable="archive_location"/>
+ <text variable="call-number"/>
+ <text macro="title"/>
+ <text macro="yearpage-bib"/>
+ </group>
+ </if>
+ <else-if type="bill chapter article-journal article-newspaper interview book graphic legal_case motion_picture paper-conference report song thesis webpage post-weblog article-magazine" match="any">
+ <group delimiter=", ">
+ <text macro="author-bib"/>
+ <text macro="title"/>
+ <text macro="translator"/>
+ <text macro="edition"/>
+ <text macro="pub-place"/>
+ <group delimiter=" ">
+ <text macro="publisher"/>
+ <text macro="collection"/>
+ </group>
+ <text macro="yearpage-bib"/>
+ </group>
+ </else-if>
+ </choose>
+ </layout>
+ </bibliography>
+</style>
diff --git a/tests/locators-delimited.expected.native b/tests/locators-delimited.expected.native
new file mode 100644
index 0000000..5500993
--- /dev/null
+++ b/tests/locators-delimited.expected.native
@@ -0,0 +1,20 @@
+Pandoc (Meta {unMeta = fromList [("csl",MetaInlines [Str "tests/locators.csl"]),("references",MetaList [MetaMap (fromList [("id",MetaInlines [Str "citekey"]),("title",MetaInlines [Str "Title"]),("type",MetaInlines [Str "article-journal"])])]),("suppress-bibliography",MetaBool True)]})
+[Para [Str "See",Space,Link ("",["uri"],[]) [Str "https://github.com/jgm/pandoc-citeproc/pull/362"] ("https://github.com/jgm/pandoc-citeproc/pull/362",""),Str "."]
+,Para [Str "Standard",Space,Str "page",Space,Str "range",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{35-89,",Space,Str "102}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 1}] [Note [Para [Str "Title",Space,Str "{35\8211\&89,",Space,Str "102}."]]]]
+,Para [Str "Alphanumeric",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{abcdefg1234}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 2}] [Note [Para [Str "Ibid-with-locator",Space,Str "{abcdefg1234}."]]]]
+,Para [Str "Kitchen",Space,Str "sink",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{123(4)a-8([a]12.398{8})}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 3}] [Note [Para [Str "Ibid-with-locator",Space,Str "{123(4)a\8211\&8([a]12.398{8})}."]]]]
+,Para [Str "Empty",Space,Str "braces",Space,Str "inside",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{{}}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 4}] [Note [Para [Str "Ibid-with-locator",Space,Str "{{}}."]]]]
+,Para [Str "Label",Space,Str "specified",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{p.\160a}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 5}] [Note [Para [Str "Ibid-with-locator",Space,Str "{a}."]]]]
+,Para [Str "Should",Space,Str "it",Space,Str "work",Space,Str "outside?",Space,Str "No.",Str "",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.",Space,Str "{(a)}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 6}] [Note [Para [Str "Subsequent,",Space,Str "p.",Space,Str "{(a)}."]]]]
+,Para [Str "Empty",Space,Str "locator",Str "",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 7}] [Note [Para [Str "Ibid."]]]]
+,Para [Str "Empty",Space,Str "locator",Space,Str "to",Space,Str "force",Space,Str "suffix",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{}",Space,Str "123-35",Space,Str "numbers",Space,Str "are",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 8}] [Note [Para [Str "Ibid",Space,Str "123-35",Space,Str "numbers",Space,Str "are",Space,Str "suffix."]]]]
+,Para [Str "Suffix",Space,Str "generally",Str "",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{123-35}",Space,Str "numbers",Space,Str "not,",Space,Str "but",Space,Str "text",Space,Str "is",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 9}] [Note [Para [Str "Ibid-with-locator",Space,Str "{123\8211\&35}",Space,Str "numbers",Space,Str "not,",Space,Str "but",Space,Str "text",Space,Str "is",Space,Str "suffix."]]]]
+,Para [Str "With",Space,Str "preceding",Space,Str "comma",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{p.\160VI}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 10}] [Note [Para [Str "Ibid-with-locator",Space,Str "{VI}."]]]]
+,Para [Str "No",Space,Str "commas",Space,Str "before",Space,Str "label",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{,",Space,Str "p.",Space,Str "(p.\160is",Space,Str "not",Space,Str "recognised)}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 11}] [Note [Para [Str "Ibid-with-locator",Space,Str "{,",Space,Str "p.",Space,Str "(p.",Space,Str "is",Space,Str "not",Space,Str "recognised)}."]]]]
+,Para [Str "Trim",Space,Str "white",Space,Str "space",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{",Space,Str "p.\160\&9",Space,Str "}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 12}] [Note [Para [Str "Ibid-with-locator",Space,Str "{9}."]]]]
+,Para [Str "Without",Space,Str "delimiters",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 13}] [Note [Para [Str "Subsequent,",Space,Str "suffix."]]]]
+,Para [Str "With",Space,Str "rendering",Space,Str "label",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{ss",Space,Str "IV",Space,Str "div",Space,Str "4",Space,Str "s",Space,Str "128L(7)(a)(i)-(iv),",Space,Str "129(5),",SoftBreak,Str "130(b)}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 14}] [Note [Para [Str "Ibid-with-locator",Space,Str "ss",Space,Str "{IV",Space,Str "div",Space,Str "4",Space,Str "s",Space,Str "128L(7)(a)(i)\8211(iv),",Space,Str "129(5),",Space,Str "130(b)}."]]]]
+,Para [Str "The",Space,Str "text",Space,Str "is",Space,Str "apparently",Space,Str "NOT",Space,Str "verbatim;",Space,Str "it",Space,Str "is",Space,Str "lightly",Space,Str "processed",Space,Str "as",Space,Str "page",Space,Str "numbers.",Str "",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{no",Space,Str "comma,",Space,Str "no",Space,Str "label,",Space,Str "no",Space,Str "nothing}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 15}] [Note [Para [Str "Ibid-with-locator",Space,Str "{no",Space,Str "comma,",Space,Str "nolabel,",Space,Str "nonothing}."]]]]
+,Para [Str "AGLC-style",Space,Str "page",Space,Str "[para]",Str "",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{584",Space,Str "[78]}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 16}] [Note [Para [Str "Ibid-with-locator",Space,Str "{584",Space,Str "[78]}."]]]]
+,Para [Str "Unbalanced",Space,Str "curly",Space,Str "{",Space,Str "breaks",Space,Str "the",Space,Str "parse",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{p.\160suffix{suffix}suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 17}] [Note [Para [Str "Subsequent",Space,Str "{p.\160suffix{suffix}suffix."]]]]
+,Para [Str "Unbalanced",Space,Str "curly",Space,Str "}",Space,Str "ends",Space,Str "early",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{green}suffix}suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 18}] [Note [Para [Str "Ibid-with-locator",Space,Str "{green}",Space,Str "suffix}suffix."]]]]]
diff --git a/tests/locators-delimited.in.native b/tests/locators-delimited.in.native
new file mode 100644
index 0000000..aee1ac0
--- /dev/null
+++ b/tests/locators-delimited.in.native
@@ -0,0 +1,20 @@
+Pandoc (Meta {unMeta = fromList [("csl",MetaInlines [Str "tests/locators.csl"]),("references",MetaList [MetaMap (fromList [("id",MetaInlines [Str "citekey"]),("title",MetaInlines [Str "Title"]),("type",MetaInlines [Str "article-journal"])])]),("suppress-bibliography",MetaBool True)]})
+[Para [Str "See",Space,Link ("",["uri"],[]) [Str "https://github.com/jgm/pandoc-citeproc/pull/362"] ("https://github.com/jgm/pandoc-citeproc/pull/362",""),Str "."]
+,Para [Str "Standard",Space,Str "page",Space,Str "range",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{35-89,",Space,Str "102}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey{35-89,",Space,Str "102}]"]]
+,Para [Str "Alphanumeric",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{abcdefg1234}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey{abcdefg1234}]"]]
+,Para [Str "Kitchen",Space,Str "sink",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{123(4)a-8([a]12.398{8})}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "{123(4)a-8(\\[a\\]12.398{8})}]"]]
+,Para [Str "Empty",Space,Str "braces",Space,Str "inside",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{{}}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "{{}}]"]]
+,Para [Str "Label",Space,Str "specified",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{p.\160a}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey{p.",Space,Str "a}]"]]
+,Para [Str "Should",Space,Str "it",Space,Str "work",Space,Str "outside?",Space,Str "No.",Space,Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.",Space,Str "{(a)}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "p.",Space,Str "{(a)}]"]]
+,Para [Str "Empty",Space,Str "locator",Space,Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "{}]"]]
+,Para [Str "Empty",Space,Str "locator",Space,Str "to",Space,Str "force",Space,Str "suffix",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{}",Space,Str "123-35",Space,Str "numbers",Space,Str "are",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey{}",Space,Str "123-35",Space,Str "numbers",Space,Str "are",Space,Str "suffix]"]]
+,Para [Str "Suffix",Space,Str "generally",Space,Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{123-35}",Space,Str "numbers",Space,Str "not,",Space,Str "but",Space,Str "text",Space,Str "is",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey{123-35}",Space,Str "numbers",Space,Str "not,",Space,Str "but",Space,Str "text",Space,Str "is",Space,Str "suffix]"]]
+,Para [Str "With",Space,Str "preceding",Space,Str "comma",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{p.\160VI}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "{p.",Space,Str "VI}]"]]
+,Para [Str "No",Space,Str "commas",Space,Str "before",Space,Str "label",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{,",Space,Str "p.",Space,Str "(p.\160is",Space,Str "not",Space,Str "recognised)}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "{,",Space,Str "p.",Space,Str "(p.",Space,Str "is",Space,Str "not",Space,Str "recognised)}]"]]
+,Para [Str "Trim",Space,Str "white",Space,Str "space",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "{",Space,Str "p.\160\&9",Space,Str "}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "{",Space,Str "p.",Space,Str "9",Space,Str "}]"]]
+,Para [Str "Without",Space,Str "delimiters",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "suffix]"]]
+,Para [Str "With",Space,Str "rendering",Space,Str "label",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{ss",Space,Str "IV",Space,Str "div",Space,Str "4",Space,Str "s",Space,Str "128L(7)(a)(i)-(iv),",Space,Str "129(5),",SoftBreak,Str "130(b)}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey{ss",Space,Str "IV",Space,Str "div",Space,Str "4",Space,Str "s",Space,Str "128L(7)(a)(i)-(iv),",Space,Str "129(5),",SoftBreak,Str "130(b)}]"]]
+,Para [Str "The",Space,Str "text",Space,Str "is",Space,Str "apparently",Space,Str "NOT",Space,Str "verbatim;",Space,Str "it",Space,Str "is",Space,Str "lightly",Space,Str "processed",Space,Str "as",Space,Str "page",Space,Str "numbers.",SoftBreak,Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{no",Space,Str "comma,",Space,Str "no",Space,Str "label,",Space,Str "no",Space,Str "nothing}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey{no",Space,Str "comma,",Space,Str "no",Space,Str "label,",Space,Str "no",Space,Str "nothing}]"]]
+,Para [Str "AGLC-style",Space,Str "page",Space,Str "[para]",SoftBreak,Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{584",Space,Str "[78]}"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey{584",Space,Str "[78]}]"]]
+,Para [Str "Unbalanced",Space,Str "curly",Space,Str "{",Space,Str "breaks",Space,Str "the",Space,Str "parse",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{p.\160suffix{suffix}suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey{p.",Space,Str "suffix{suffix}suffix]"]]
+,Para [Str "Unbalanced",Space,Str "curly",Space,Str "}",Space,Str "ends",Space,Str "early",Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str "{green}suffix}suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey{green}suffix}suffix]"]]]
diff --git a/tests/locators-integrated.expected.native b/tests/locators-integrated.expected.native
new file mode 100644
index 0000000..6b973d5
--- /dev/null
+++ b/tests/locators-integrated.expected.native
@@ -0,0 +1,26 @@
+Pandoc (Meta {unMeta = fromList [("csl",MetaInlines [Str "tests/locators.csl"]),("references",MetaList [MetaMap (fromList [("id",MetaInlines [Str "citekey"]),("title",MetaInlines [Str "Title"]),("type",MetaInlines [Str "article-journal"])])]),("suppress-bibliography",MetaBool True)]})
+[Para [Str "See",Space,Link ("",["uri"],[]) [Str "https://github.com/jgm/pandoc-citeproc/pull/362"] ("https://github.com/jgm/pandoc-citeproc/pull/362",""),Str "."]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "89,",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 1}] [Note [Para [Str "Title",Space,Str "{89},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "89,",Space,Str "perfect",Space,Str "Ibid",Space,Str "with",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 2}] [Note [Para [Str "Ibid,",Space,Str "perfect",Space,Str "Ibid",Space,Str "with",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "123-79,",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 3}] [Note [Para [Str "Ibid-with-locator",Space,Str "{123\8211\&79},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "xi,",Space,Str "will",Space,Str "be",Space,Str "entirely",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 4}] [Note [Para [Str "Subsequent,",Space,Str "xi,",Space,Str "will",Space,Str "be",Space,Str "entirely",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.\160xi,",Space,Str "gives",Space,Str "you",Space,Str "a",Space,Str "(page)",Space,Str "locator",Space,Str "xi"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 5}] [Note [Para [Str "Ibid-with-locator",Space,Str "{xi},",Space,Str "gives",Space,Str "you",Space,Str "a",Space,Str "(page)",Space,Str "locator",Space,Str "xi."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "pp.\160VII,",Space,Str "89,",Space,Str "gives",Space,Str "you",Space,Str "a",Space,Str "(pages)",Space,Str "locator",Space,Str "VII,",Space,Str "89"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 6}] [Note [Para [Str "Ibid-with-locator",Space,Str "{VII,",Space,Str "89},",Space,Str "gives",Space,Str "you",Space,Str "a",Space,Str "(pages)",Space,Str "locator",Space,Str "VII,",Space,Str "89."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.\160VI,",Space,Str "VII,",Space,Str "VIII-IX,",Space,Str "explicit",Space,Str "romans"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 7}] [Note [Para [Str "Ibid-with-locator",Space,Str "{VI,",Space,Str "VII,",Space,Str "VIII\8211IX},",Space,Str "explicit",Space,Str "romans."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Space,Str "[89]"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 8}] [Note [Para [Str "Ibid-with-locator",Space,Str "{[89]}."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.",Space,Str "[89]"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 9}] [Note [Para [Str "Ibid."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Space,Str "and",Space,Str "nothing",Space,Str "else"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 10}] [Note [Para [Str "Subsequent",Space,Str "and",Space,Str "nothing",Space,Str "else."]]],Str "."]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "123(4)[5]6,",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 11}] [Note [Para [Str "Ibid-with-locator",Space,Str "{123(4)[5]6},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "3(a),",Space,Str "4.4.8,",Space,Str "[7.6],",Space,Str "7A(2)(a)(i)-(iv)"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 12}] [Note [Para [Str "Ibid-with-locator",Space,Str "{3(a),",Space,Str "4.4.8,",Space,Str "[7.6],",Space,Str "7A(2)(a)(i)\8211(iv)}."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "4B.2a.i(3.4),",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 13}] [Note [Para [Str "Ibid-with-locator",Space,Str "{4B.2a.i(3.4)},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "IV.2A,",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 14}] [Note [Para [Str "Ibid-with-locator",Space,Str "{IV.2A},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "[28],",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 15}] [Note [Para [Str "Ibid-with-locator",Space,Str "{[28]},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "[39-52],",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 16}] [Note [Para [Str "Ibid-with-locator",Space,Str "{[39\8211\&52]},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "[39]-[52],",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 17}] [Note [Para [Str "Ibid-with-locator",Space,Str "{[39]\8211[52]},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "s",Space,Str "123(4)(a)(iv),",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 18}] [Note [Para [Str "Ibid-with-locator",Space,Str "s",Space,Str "{123(4)(a)(iv)},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "ss",Space,Str "123(4)-(6),",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 19}] [Note [Para [Str "Ibid-with-locator",Space,Str "ss",Space,Str "{123(4)\8211(6)},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "[13],",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 20}] [Note [Para [Str "Ibid-with-locator",Space,Str "{[13]},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.3,",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 21}] [Note [Para [Str "Ibid-with-locator",Space,Str "{3},",Space,Str "and",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "(13",Space,Str "entirely",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 22}] [Note [Para [Str "Subsequent,",Space,Str "(13",Space,Str "entirely",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.a",Space,Str "entirely",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 23}] [Note [Para [Str "Ibid,",Space,Str "p.a",Space,Str "entirely",Space,Str "suffix."]]]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "s",Space,Str "(a)",Space,Str "entirely",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 24}] [Note [Para [Str "Ibid,",Space,Str "s",Space,Str "(a)",Space,Str "entirely",Space,Str "suffix."]]]]]
diff --git a/tests/locators-integrated.in.native b/tests/locators-integrated.in.native
new file mode 100644
index 0000000..9ebb8cd
--- /dev/null
+++ b/tests/locators-integrated.in.native
@@ -0,0 +1,26 @@
+Pandoc (Meta {unMeta = fromList [("csl",MetaInlines [Str "tests/locators.csl"]),("references",MetaList [MetaMap (fromList [("id",MetaInlines [Str "citekey"]),("title",MetaInlines [Str "Title"]),("type",MetaInlines [Str "article-journal"])])]),("suppress-bibliography",MetaBool True)]})
+[Para [Str "See",Space,Link ("",["uri"],[]) [Str "https://github.com/jgm/pandoc-citeproc/pull/362"] ("https://github.com/jgm/pandoc-citeproc/pull/362",""),Str "."]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "89,",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "89,",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "89,",Space,Str "perfect",Space,Str "Ibid",Space,Str "with",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "89,",Space,Str "perfect",Space,Str "Ibid",Space,Str "with",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "123-79,",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "123-79,",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "xi,",Space,Str "will",Space,Str "be",Space,Str "entirely",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "xi,",Space,Str "will",Space,Str "be",Space,Str "entirely",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.\160xi,",Space,Str "gives",Space,Str "you",Space,Str "a",Space,Str "(page)",Space,Str "locator",Space,Str "xi"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "p.\160xi,",Space,Str "gives",Space,Str "you",Space,Str "a",Space,Str "(page)",Space,Str "locator",Space,Str "xi]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "pp.\160VII,",Space,Str "89,",Space,Str "gives",Space,Str "you",Space,Str "a",Space,Str "(pages)",Space,Str "locator",Space,Str "VII,",Space,Str "89"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "pp.\160VII,",Space,Str "89,",Space,Str "gives",Space,Str "you",Space,Str "a",Space,Str "(pages)",Space,Str "locator",Space,Str "VII,",Space,Str "89]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.\160VI,",Space,Str "VII,",Space,Str "VIII-IX,",Space,Str "explicit",Space,Str "romans"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "p.\160VI,",Space,Str "VII,",Space,Str "VIII-IX,",Space,Str "explicit",Space,Str "romans]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Space,Str "[89]"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey",Space,Str "\\[89\\]]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.",Space,Str "[89]"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "p.",Space,Str "\\[89\\]]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Space,Str "and",Space,Str "nothing",Space,Str "else"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey",Space,Str "and",Space,Str "nothing",Space,Str "else]"],Str "."]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "123(4)[5]6,",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "123(4)\\[5\\]6,",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "3(a),",Space,Str "4.4.8,",Space,Str "[7.6],",Space,Str "7A(2)(a)(i)-(iv)"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "3(a),",Space,Str "4.4.8,",Space,Str "\\[7.6\\],",Space,Str "7A(2)(a)(i)-(iv)]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "4B.2a.i(3.4),",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "4B.2a.i(3.4),",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "IV.2A,",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "IV.2A,",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "[28],",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "\\[28\\],",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "[39-52],",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "\\[39-52\\],",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "[39]-[52],",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "\\[39\\]-\\[52\\],",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "s",Space,Str "123(4)(a)(iv),",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "s",Space,Str "123(4)(a)(iv),",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "ss",Space,Str "123(4)-(6),",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "ss",Space,Str "123(4)-(6),",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "[13],",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "\\[13\\],",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.3,",Space,Str "and",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "p.3,",Space,Str "and",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "(13",Space,Str "entirely",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "(13",Space,Str "entirely",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "p.a",Space,Str "entirely",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "p.a",Space,Str "entirely",Space,Str "suffix]"]]
+,Para [Cite [Citation {citationId = "citekey", citationPrefix = [], citationSuffix = [Str ",",Space,Str "s",Space,Str "(a)",Space,Str "entirely",Space,Str "suffix"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@citekey,",Space,Str "s",Space,Str "(a)",Space,Str "entirely",Space,Str "suffix]"]]]
diff --git a/tests/locators.csl b/tests/locators.csl
new file mode 100644
index 0000000..2484c68
--- /dev/null
+++ b/tests/locators.csl
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<style xmlns="http://purl.org/net/xbiblio/csl" class="note" version="1.0" demote-non-dropping-particle="sort-only" default-locale="en-GB">
+ <info>
+ <title>A CSL to test locator parsing in pandoc-citeproc</title>
+ <title-short>test-locators</title-short>
+ <id>https://example.com/test-locators</id>
+ <link href="https://forums.zotero.org/discussion/4841/new-australian-legal-citation-style/?Focus=20831#Comment_20831" rel="documentation"/>
+ <category citation-format="note"/>
+ </info>
+
+ <locale>
+ <terms>
+ <term name="section" form="short">
+ <single>s</single>
+ <multiple>ss</multiple>
+ </term>
+ </terms>
+ </locale>
+
+ <macro name="locator">
+ <choose>
+ <if locator="section">
+ <!-- s or ss -->
+ <group delimiter=" ">
+ <label variable="locator" form="short" />
+ <text variable="locator" prefix="{" suffix="}" />
+ </group>
+ </if>
+ <else>
+ <text variable="locator" prefix="{" suffix="}" />
+ </else>
+ </choose>
+ </macro>
+
+ <macro name="title">
+ <text variable="title" suffix=" "/>
+ </macro>
+
+ <citation>
+ <layout prefix="" suffix="." delimiter="; ">
+ <choose>
+ <if position="ibid-with-locator">
+ <text value="ibid-with-locator "/>
+ <text macro="locator"/>
+ </if>
+ <else-if position="ibid">
+ <text value="ibid"/>
+ </else-if>
+ <else-if position="subsequent">
+ <text value="subsequent "/>
+ <text macro="locator"/>
+ </else-if>
+ <else>
+ <text macro="title" />
+ <text macro="locator" />
+ </else>
+ </choose>
+ </layout>
+ </citation>
+ <bibliography>
+ <sort>
+ <key variable="issued"/>
+ </sort>
+ <layout>
+ <text variable="title" />
+ </layout>
+ </bibliography>
+</style>