How to extract all bibliographic citation keys from a Typst file?

Hello! I use (and am one of the maintainers of) a bibliography auto-generation command-line tool called Autobib. Essentially, given a list of citation keys in a specific format, Autobib generates a .bib file containing exactly those citation keys, with data either from a local cache or retrieved from a remote API.

The most convenient option is for Autobib to be able to automatically retrieve all citation keys directly from a file. However, I am struggling to understand how to best integrate this with Typst.

In this PR I implemented very basic support using the typst-syntax crate to find all explicit #cite function calls and to extract label text. This works, but it is somewhat fragile:

  1. It misses custom citation syntax #let c = cite and #c(<ref>), label text generated by another function, etc.
  2. It does not handle @ref at all: I do not know how to distinguish existing internal links, undefined internal links, and genuine citation keys.

I also tried typst query file.typ cite --field key, but undefined citation keys result in an error, and the whole point of Autobib is to programmatically retrieve the data for undefined citation keys.

Finally, I looked a bit into implementing this as a plugin. An ideal solution would be to define a special function, say #autocite, such that one can call #autocite(<ref>) and then this calls back into Autobib to generate the bibliographic data and then inserts a #cite(<ref>) in-place, before bibliography parsing occurs. But it seems that calling into external code is not really a supported use-case for plugins, and moreover the above pattern might not compatible with the Typst compilation model.

I am curious if there are any suggestions for the best way to integrate Autobib into Typst? I appreciate any thoughts about this, thank you!

Well, I don’t quite understand your situation, but you can wrap the original *.typ with some hacks and then typst query will work fine.

// Convert (possibly broken) citations to metadata.
#show ref: it => {
  if it.element == none {
    [#metadata(it)<hack>]
  } else {
    it
  }
}
#show cite: it => [#metadata(it)<hack>]

// -----------------------
// Start of the original *.typ

#set heading(numbering: "1.1")
= Internal <internal>

Ignored internal: @internal.

@zbl:1337.28015
#cite(label("doi:10.4007/annals.2014.180.2.7"))
@mr:3224722[p.~7]

`#cite(<raw>)`

// End of the original *.typ
// -----------------------

#bibliography(bytes("")) // Create an empty bibliography source
$ typst query a.typ '<hack>' --format yaml
- func: metadata
  value:
    func: ref
    target: <zbl:1337.28015>
    supplement: auto
    form: normal
    citation:
      func: cite
      key: <zbl:1337.28015>
      supplement: null
    element: null
  label: <hack>
- func: metadata
  value:
    func: cite
    key: label("doi:10.4007/annals.2014.180.2.7")
    supplement: null
    form: normal
    style: auto
  label: <hack>
- func: metadata
  value:
    func: ref
    target: <mr:3224722>
    supplement:
      func: sequence
      children:
      - func: text
        text: p.
      - func: symbol
        text:  
      - func: text
        text: '7'
    form: normal
    citation:
      func: cite
      key: <mr:3224722>
      supplement:
        func: sequence
        children:
        - func: text
          text: p.
        - func: symbol
          text:  
        - func: text
          text: '7'
    element: null
  label: <hack>

Besides, you might want to pause the integration until typst v0.15.0, because typst query will be replaced with the more general typst eval. See 0.15.0-rc.1 - Typst Documentation or Add `typst eval` CLI command by TimerErTim · Pull Request #7362 · typst/typst · GitHub.

$ typst-dev eval --in a.typ --format yaml 'query(<hack>).map(x => x.value).map(c => str(c.at("target", default: c.at("key", default: none))))'
- zbl:1337.28015
- doi:10.4007/annals.2014.180.2.7
- mr:3224722

Edit: See Release v0.15.0 · typst/typst · GitHub

3 Likes

Thank you for the detailed response! This solution using typst query works very well. Thanks for the heads up about typst eval as well.

To rephrase my original question in case someone else looks at this, I was wondering about the best way to extract all bibliographic citation keys from a Typst file, whether or not they are defined in a bibliography.