JuliaDocs / DocumenterCitations.jl

DocumenterCitations.jl uses Bibliography.jl to add support for BibTeX citations and references in documentation pages generated by Documenter.jl.
https://juliadocs.github.io/DocumenterCitations.jl/
MIT License
72 stars 5 forks source link

How to use `:numeric` but with `:nyt` sorting #29

Open nathanaelbosch opened 1 year ago

nathanaelbosch commented 1 year ago

I followed this part of the documentation to try to get the list of references sorted in a name-year-title manner, but it seems that when using :numeric all cited references are still sorted in order of appearence, and only the non-cited references are then sorted. Is there a way to sort all references in a name-year-title manner, even when using :numeric? I also only use the *-type bibliography as I want to have one extensive list of references.

goerz commented 1 year ago

Well, in principle, the sorting is very much inherent in the :numeric style. We closely follow LaTeX's behavior with the styles. I think for your use case, one would normally use the :alpha style. Right now that style has a few problems, but we'll fix that shortly.

That being said, you can overwrite the sorting for the :numeric style by putting

import DocumenterCitations: bib_sorting
bib_sorting(::Val{:numeric}) = :nyt

in your docs/make.jl file. This is not part of the public API, though, and thus might change in future releases.

nathanaelbosch commented 1 year ago

Thanks for the reply! I tried overwriting bib_sorting and it indeed then sorts as I'd like, but the numbering does not match. I also tried overwriting format_bibliography_label https://github.com/JuliaDocs/DocumenterCitations.jl/blob/04ed9745225220872a9a2e01f8710c6101147e37/src/bibliography.jl#L103-L116 with some custom code, but I could not get to the proper result, as the inputs entry and citations do not really provide sufficient information to implement this. Instead the function would need to have access to the whole bibliography. Is there any way this could be provided such that such a citation style would be possible? If an alphabetic style with numeric labels would be of interest I would be happy to make a PR for this.

goerz commented 1 year ago

but the numbering does not match

The numbers in the citations and bibliography entries don't match? Did you restart your Julia session? I don't seem to be able to reproduce any mismatch…

Or, are you just saying you want the bibliography to be numbered in order, but the citations to be jumbled? That would be a very non-standard citation style. You really should be using :alpha.

If you did want to implement such a style, you would have to define a struct, e.g., EnumeratedStyle that would be instantiated as EnumeratedStyle(bibfile) and passed as style to CitationBibliography. It would read in the bib file, sort the entries, and create a dict that maps BibTex keys (entry.id). You would then define [format_citation](https://juliadocs.org/DocumenterCitations.jl/dev/internals/#DocumenterCitations.format_citation), format_bibliography_label, etc. to use that dict when rendering the labels.

This is the same approach that I just used for the improved :alpha style: https://github.com/JuliaDocs/DocumenterCitations.jl/pull/31

If an alphabetic style with numeric labels would be of interest I would be happy to make a PR for this

No, if you want to use such as style for your project, you can do that. But I would strongly discourage anyone from using this style, as it runs against all established conventions. As I said, it sounds like what you should be using is :alpha.

nathanaelbosch commented 1 year ago

If I use :numeric, but set the ordering to :nyt, then the numbering is in the order of appearance, but the actual bibliography list is ordered by author. This means the numbers don't appear in order in the bibliography.

What I'd like to have is a bibliography ordered by author-year, but compact citations as numbers according to this author-year ordering. This does not seem highly non-standard, it is even the very first example that appears in this overleaf article on "Bibliography management in LaTeX".

goerz commented 1 year ago

Huh, okay… I must have suppressed that the LaTeX style plain also does this. It must be a field-specific bias that this looks just utterly wrong to me. Alas, it seems like this is somewhat common in computer science: The SIAM journals use the style you describe, including the original Julia paper, which sets a sufficient precedent for to me to consider accepting a PR implementing such as style.

In any case, this will require a "stateful" style, i.e., an object (not just a symbol) to be passed as style. In your case, where you want everything in the .bib file to appear in your bibliography, this could be done exactly as in https://github.com/JuliaDocs/DocumenterCitations.jl/pull/31 where the labels are determined up-front when the style is instantiated.

More generally, though, if you don't have the entire .bib file appear in the bibliography, and especially if you have multiple bibliography blocks, you'd need a slightly different approach. I would tie in to format_bibliography_label: the first time it gets called, it could calculate the label for every cited reference and cache the result in a field of the style object.

I'm going to think about this more, but maybe I'll change how the internal format_* functions are called, and pass them the full CitationBibliography object instead of just the citations dict. Having access to all entries would certainly make it a lot less cumbersome to implement a style like this.

goerz commented 1 year ago

If found a more elegant solution in https://github.com/JuliaDocs/DocumenterCitations.jl/pull/31#issuecomment-1705522626 with a new internal init_bibliography! function.

Assuming the current #31 is merged, putting the following in docs/make.jl should do what you want:

DocumenterCitations.bib_html_list_style(style::Val{:plain}) = :dl
DocumenterCitations.bib_sorting(style::Val{:plain}) = :nyt
DocumenterCitations.format_bibliography_label(style::Val{:plain}, entry, citations) =
    DocumenterCitations.format_bibliography_label(:numeric, entry, citations)
DocumenterCitations.format_bibliography_reference(style::Val{:plain}, entry) =
    DocumenterCitations.format_bibliography_reference(:numeric, entry)
DocumenterCitations.format_citation(style::Val{:plain}, entry, citations; kwargs...) =
    DocumenterCitations.format_citation(:numeric, entry, citations; kwargs...)

using Bibliography: sort_bibliography!

function DocumenterCitations.init_bibliography!(style::Val{:plain}, bib)
    sort_bibliography!(bib.entries, :nyt)
    # overwrite citations number
    for (i, (key, entry)) in enumerate(bib.entries)
        bib.citations[key] = i
    end
end

bib = CitationBibliography(
    joinpath(@__DIR__, "src", "refs.bib");
    style=:plain  # using the custom style
)

This simply overwrites the numeric citation keys with the number of the entry after sorting. Since that marks every entry in the .bib file as "cited", this effectively restores the pre-1.0 behavior where

```@bibliography
```

is equivalent to

```@bibliography
*
```

Alternatively, you could filter to only existing keys in bib.citations but then you'd run into trouble if you have any @bibliography block that contains entries that aren't otherwise cited (e.g., via * or an explicit list of keys).

I'm not totally sure how easy it would be to implement this style for full generality. If someone figures out a complete solution, I'd consider merging a PR for a :plain style.