hayagriva icon indicating copy to clipboard operation
hayagriva copied to clipboard

Alphanumeric produce different cite keys in text and bibliography

Open tkw1536 opened this issue 1 year ago • 3 comments

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:

Screenshot 2024-12-08 at 12 54 30

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

tkw1536 avatar Dec 08 '24 12:12 tkw1536

Seems to be a regression from https://github.com/typst/hayagriva/pull/234

PgBiel avatar Feb 04 '25 00:02 PgBiel

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
}

Marcono1234 avatar Jun 27 '25 16:06 Marcono1234

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:date in the cite and in the bibliographic entry, but its location can be controlled by explicitly rendering the “year-suffix” variable using cs:text. If “year-suffix” is rendered through cs:text in the scope of cs:citation, it is suppressed for cs:bibliography, unless it is also rendered through cs:text in the scope of cs: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 -->

Marcono1234 avatar Jul 23 '25 12:07 Marcono1234