Alphanumeric produce different cite keys in text and bibliography
Description
I am using the alphanumeric citation style. When citing two entries from a bib file that would produce the same key (e.g. same author and same year), they render with an a and b suffix where they are cited. However, in the bibliography, they appear without the same suffix.
Without manual tweaking, with #show rules and the like I was expecting the same label to be used in both places.
As a minimal example, consider the following bib file:
@article{first,
title={First Thing},
year={2024},
author={Jane Doe}
}
@article{second,
title={Second Thing},
year={2024},
author={Jane Doe}
}
The alphanumeric cite styles assigns both articles the key Doe24.
Using the following main file:
#set cite(style: "alphanumeric")
I cite the first thing #cite(<first>) and the second thing #cite(<second>).
#bibliography("example.bib")
The pdf renders as:
Notice how the keys listed in the main body are [Doe24a] and [Doe24b] whereas in the bibliography section they are both shown as [Doe24]. There also appears to be an extra an and b at the end of the entries.
I did find two related issues, but neither of them seem to cover exactly this.
- https://github.com/typst/typst/issues/3986, but I'm not using
bibliography.full - https://github.com/typst/typst/issues/2707, but that's closed and this is still a problem
Reproduction URL
https://typst.app/project/rY9ipQQ8EYDQ3hjCEXMy22
Operating system
macOS
Typst version
- [X] I am using the latest version of Typst
Seems to be a regression from https://github.com/typst/hayagriva/pull/234
Here is a super hacky workaround which adds the a, b, ... suffix to the cite keys in the bibliography. This is for Typst 0.13.1 with bibliography(style: "ieee") and #set cite(style: "alphanumeric"). It (heavily) relies on implementation details of how Typst displays the bibliography, and might be rather brittle.
However, for my use case it seems to work; so maybe it is useful for others as well.
Workaround code (click to expand)
#show bibliography: bib => {
show grid: g => {
let keys = (:)
let duplicate-keys = ()
for (i, cell) in g.children.enumerate() {
// Assumes that bibliography grid has two columns, and key is in the first column
if calc.rem(i, 2) != 0 {
continue
}
let key = cell.body.child.text
let existing-i = keys.at(key, default: none)
if existing-i == none {
keys.insert(key, i)
} else {
duplicate-keys.push((i, key))
if not duplicate-keys.contains((existing-i, key)) {
duplicate-keys.push((existing-i, key))
}
}
}
let cells-to-replace = ()
for (i, key) in duplicate-keys {
let key-year-match = key.match(regex("(\d+)\]$"))
let key-year = key-year-match.captures.at(0)
// Assumes that bibliography grid has two columns, and value is in the second column
let entry-values = g.children.at(i + 1).body.children
let year-suffixes = entry-values
.filter(v => v.has("text"))
.map(v => {
// Extract the disambiguation suffix letter, e.g. "a", "b", ...
// To reduce false positives ideally would include check for `key-year` here; however it seems
// for some bib entry types (e.g. `type: web`) the date is not included in the text and instead
// the disambiguation letter is added to the "Accessed" year
let match = v.text.match(regex("\b\d{4}([a-z])\b"))
if match == none { none } else { (match.text, match.captures.at(0)) }
})
.filter(v => v != none)
if year-suffixes.len() == 0 {
panic("failed finding year for key " + key)
}
if year-suffixes.len() > 1 {
panic("ambiguous year for key " + key)
}
let (year-with-suffix, year-suffix) = year-suffixes.at(0)
let year-without-suffix = year-with-suffix.slice(0, year-with-suffix.len() - year-suffix.len())
let new-key = (
key.slice(0, key-year-match.start)
+ key-year
+ year-suffix
+ key.slice(key-year-match.start + key-year.len()) // Add everything which was not part of the capture
)
cells-to-replace.push((i, key, new-key, year-with-suffix, year-without-suffix))
}
show grid.cell: cell => {
for (i, key, new-key, year-with-suffix, year-without-suffix) in cells-to-replace {
// Assumes that bibliography grid has two columns
if i / 2 == cell.y {
if cell.x == 0 {
// Replace the key (add suffix)
return {
show key: new-key
cell
}
} else {
// Replace the year (remove suffix)
return {
show regex("\b" + year-with-suffix + "\b"): year-without-suffix
cell
}
}
}
}
return cell
}
g
}
bib
}
https://docs.citationstyles.org/en/stable/specification.html#disambiguation ("disambiguate-add-year-suffix [Method (4)]") says regarding this:
By default, the year-suffix is appended the first year rendered through
cs:datein the cite and in the bibliographic entry, but its location can be controlled by explicitly rendering the “year-suffix” variable usingcs:text. If “year-suffix” is rendered throughcs:textin the scope ofcs:citation, it is suppressed forcs:bibliography, unless it is also rendered throughcs:textin the scope ofcs:bibliography, and vice versa.
I might be misunderstanding it, but to me it seems currently for Hayagriva the short form date in the citation label is not recognized as date. Though on the other hand I am not sure if the CSL specification requires an explicit use of cs:date or if the implicit use through the citation label is enough.
Maybe Hayagriva's alphanumeric.csl also wouldn't need the explicit <text variable="year-suffix" /> then; in fact according to the specification quoted above the explicit use of year-suffix for the cite should suppress implicit usage of the suffix in the bibliography (which is currently not happening for Hayagriva either?).
So maybe a proper workaround for now is using a custom CSL file, e.g. something like this (based on IEEE):
CSL file snippet (click to expand)
(Might not follow best practices, and also seems to break backlinks from bibliography to first cite occurrence.)...
<!-- Citation label with disambiguation suffix, see related https://github.com/typst/hayagriva/issues/255 -->
<macro name="citation-label-with-suffix">
<group>
<text variable="citation-label"/>
<text variable="year-suffix"/>
</group>
</macro>
<!-- Citation -->
<!-- Based on https://github.com/typst/hayagriva/pull/307, not yet available in latest Typst release-->
<citation collapse="citation-number" after-collapse-delimiter="; "
disambiguate-add-year-suffix="true">
<sort>
<key variable="author"/>
<key variable="issued"/>
</sort>
<layout prefix="[" suffix="]" delimiter=", ">
<group delimiter=", ">
<text macro="citation-label-with-suffix"/>
<text variable="locator"/>
</group>
</layout>
</citation>
<!-- Bibliography -->
<bibliography entry-spacing="0">
<sort>
<key macro="citation-label-with-suffix"/>
</sort>
<layout>
<!-- Citation Key -->
<!-- TODO: Once https://github.com/typst/hayagriva/issues/345 is fixed remove
`display="left-margin"` and use `second-field-align="flush"` again, otherwise
backlinks from bibliography to first cite occurrence don't work
-->
<text macro="citation-label-with-suffix" prefix="[" suffix="]" display="left-margin"/>
... <!-- remaining bib entry data -->