diff --git a/NEWS.md b/NEWS.md index 0fa8579..b866f5b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Fixed + +* Do not try include a year when rendering a reference if the underlying BibTeX entry has no `year` field. +* Avoid duplicate labels in `:alpha` style. This is implemented via the new stateful `AlphaStyle()`, but is handled automatically. +* With the alphabetic style (`:alpha`/`AlphaStyle`), include up to 4 names in the label, not 3 (but 5 or more names results in 3 names and "+"). Also, include the first letter of a "particle" in the label, e.g. "vWB08" for a first author "von Winckel". Both of these are consistent with LaTeX's behavior. + +### Added + +* New `style=AlphaStyle()` that generates unique citation labels. This can mostly be considered internal, as `style=:alpha` is automatically upgraded to `style=AlphaStyle()`. + +### Internal Changes + +* Added an internal function `init_bibliography!` that is called at the beginning of the `ExpandBibliography` pipeline step. This function is intended to initialized internal state either of the `style` object or the `CitationBibliography` plugin object before rendering any `@bibliography` blocks. This is used to generate unique citation labels for the new `AlphaStyle()`. For the other builtin styles, it is a no-op. Generally, `init_bibliography!` can help with implementing custom "stateful" styles. + ## [Version v1.0.0][1.0.0] - 2023-07-12 diff --git a/docs/latex/Makefile b/docs/latex/Makefile index 17b72f3..d7bdbfc 100644 --- a/docs/latex/Makefile +++ b/docs/latex/Makefile @@ -1,6 +1,6 @@ .PHONY: all clean -ALL = numeric.pdf authoryear.pdf rmp.pdf prb.pdf +ALL = numeric.pdf authoryear.pdf rmp.pdf prb.pdf alpha.pdf all: $(ALL) @@ -28,6 +28,12 @@ prb.pdf: prb.tex pdflatex $< pdflatex $< +alpha.pdf: alpha.tex + pdflatex $< + bibtex $(basename $<) + pdflatex $< + pdflatex $< + clean: @rm -f ${ALL} @rm -f *.aux diff --git a/docs/latex/alpha.tex b/docs/latex/alpha.tex new file mode 100644 index 0000000..48a8c77 --- /dev/null +++ b/docs/latex/alpha.tex @@ -0,0 +1,63 @@ +\documentclass[aps,pra,onecolumn,noshowpacs,superscriptaddress,preprintnumbers,% +kamsmath,amssymb,notitlepage,letterpaper]{revtex4-2} + +\def\Author{Michael Goerz} +\def\Title{Demo of the standard RevTeX numeric citation style} + +\usepackage{natbib} +\usepackage[utf8]{inputenc} +\usepackage{caption} +\captionsetup{justification=raggedright, singlelinecheck=true} +\usepackage[ + pdftitle={\Title}, + pdfauthor={\Author}, + colorlinks=true, linkcolor=black, urlcolor=black, citecolor=black, + bookmarksopen=false, breaklinks=true, plainpages=false, pdfpagelabels +]{hyperref} + +\begin{document} + +\title{\Title} +\author{\Author} +\date{\today} + +\maketitle + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\begin{itemize} + \item \verb|\cite{GoerzQ2022}| renders as ``\cite{GoerzQ2022}''. + \item \verb|\citet{GoerzQ2022}| renders as ``\citet{GoerzQ2022}''. + \item \verb|\citep{GoerzQ2022}| renders as ``\citep{GoerzQ2022}''. + \item \verb|\cite[Eq.~(1)]{GoerzQ2022}| renders as ``\cite[Eq.~(1)]{GoerzQ2022}''. + \item \verb|\citet[Eq.~(1)]{GoerzQ2022}| renders as ``\citet[Eq.~(1)]{GoerzQ2022}''. + \item \verb|\citep[Eq.~(1)]{GoerzQ2022}| renders as ``\citep[Eq.~(1)]{GoerzQ2022}''. + \item \verb|\citet*{GoerzQ2022}| renders as ``\citet*{GoerzQ2022}''. + \item \verb|\citep*{GoerzQ2022}| renders as ``\citep*{GoerzQ2022}''. + \item \verb|\citet*[Eq.~(1)]{GoerzQ2022}| renders as ``\citet*[Eq.~(1)]{GoerzQ2022}''. + \item \verb|\citep*[Eq.~(1)]{GoerzQ2022}| renders as ``\citep*[Eq.~(1)]{GoerzQ2022}''. + \item \verb|\citet{WinckelIP2008}| renders as ``\citet{WinckelIP2008}''. + \item \verb|\Citet{WinckelIP2008}| renders as ``\Citet{WinckelIP2008}''. + \item \cite{GraceJMO2007}, \cite{GraceJPB2007}, \cite{GrondPRA2009a}, and \cite{GrondPRA2009b} +\end{itemize} + +Further commands that we do not support in markdown: + +\begin{itemize} + \item \verb|\citenum{GoerzQ2022}| renders as ``\citenum{GoerzQ2022}''. + \item \verb|\citealt{GoerzQ2022}| renders as ``\citealt{GoerzQ2022}''. + \item \verb|\citealp{GoerzQ2022}| renders as ``\citealp{GoerzQ2022}''. + \item \verb|\citealt*{GoerzQ2022}| renders as ``\citealt*{GoerzQ2022}''. + \item \verb|\citealp*{GoerzQ2022}| renders as ``\citealp*{GoerzQ2022}''. + \item \verb|\citealt*[Eq.~(1)]{GoerzQ2022}| renders as ``\citealt*[Eq.~(1)]{GoerzQ2022}''. + \item \verb|\citealp*[Eq.~(1)]{GoerzQ2022}| renders as ``\citealp*[Eq.~(1)]{GoerzQ2022}''. + \item \verb|\Citealt{WinckelIP2008}| renders as ``\Citealt{WinckelIP2008}''. + \item \verb|\Citealp{WinckelIP2008}| renders as ``\Citealp{WinckelIP2008}''. +\end{itemize} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\bibliographystyle{alpha} +\bibliography{../src/refs} + +\end{document} diff --git a/docs/src/gallery.md b/docs/src/gallery.md index a796ee0..c66f0b4 100644 --- a/docs/src/gallery.md +++ b/docs/src/gallery.md @@ -82,6 +82,24 @@ Style = :alpha Canonical = false ``` +Note that the `:alpha` style is able to automatically disambiguate labels: + +```@bibliography +Pages = [] +Style = :alpha +Canonical = false + +GraceJMO2007 +GraceJPB2007 +``` + +This works because the `DocumenterCitations` automatically upgrades `style=:alpha` to the internal + +```@docs +DocumenterCitations.AlphaStyle +``` + + ## [Custom styles](@id custom_styles) In the following, we show two examples for user-defined styles. See the [notes on customization](@ref customization) on how to generally define a custom style. diff --git a/docs/src/internals.md b/docs/src/internals.md index e109ddb..0884643 100644 --- a/docs/src/internals.md +++ b/docs/src/internals.md @@ -27,7 +27,7 @@ ExpandCitations ## [Customization](@id customization) -A custom style can be created by defining methods for functions listed below that specialize for a user-defined `style` argument to [`CitationBibliography`](@ref). If the `style` is identified by a simple name, e.g. `:mystyle`, the method should specialize on `Val{:mystyle}`, see the [examples for custom styles](@ref custom_styles). Beyond that, e.g., if the `style` needs to implement options or needs to maintain internal state to manage unique citation labels, `style` can be an object of a custom type. +A custom style can be created by defining methods for the functions listed below that specialize for a user-defined `style` argument to [`CitationBibliography`](@ref). If the `style` is identified by a simple name, e.g. `:mystyle`, the methods should specialize on `Val{:mystyle}`, see the [examples for custom styles](@ref custom_styles). Beyond that, e.g., if the `style` needs to implement options or needs to maintain internal state to manage unique citation labels, `style` can be an object of a custom type. The builtin [`DocumenterCitations.AlphaStyle`](@ref) is an example for such a "stateful" style, initialized via a custom [`init_bibliography!`](@ref) method. ```@docs @@ -36,6 +36,7 @@ bib_sorting format_bibliography_label format_bibliography_reference format_citation +init_bibliography! ``` ## Debugging diff --git a/docs/src/refs.bib b/docs/src/refs.bib index 5600c78..dc1e786 100644 --- a/docs/src/refs.bib +++ b/docs/src/refs.bib @@ -312,6 +312,8 @@ @article{GoerzQ2022 Doi = {10.22331/q-2022-12-07-871}, Pages = {871}, Volume = {6}, + eprint = {2205.15044}, + Archiveprefix = {arXiv}, } @article{RaithelQST2022, @@ -427,3 +429,59 @@ @manual{SciPy year = {2001--}, url = {https://docs.scipy.org/doc/scipy/}, } + + +@article{LapertPRA09, + Author = {Lapert, M. and Tehini, R. and Turinici, G. and Sugny, D.}, + Title = {Monotonically Convergent Optimal Control Theory of Quantum Systems with Spectral Constraints on the Control Field}, + Journal = pra, + Year = {2009}, + Doi = {10.1103/PhysRevA.79.063411}, + Pages = {063411}, + Volume = {79}, +} + + +@article{GraceJMO2007, + Author = {Grace, Matthew D. and Brif, Constantin and Rabitz, Herschel and Lidar, Daniel A. and Walmsley, Ian A. and Kosut, Robert L.}, + Title = {Fidelity of optimally controlled quantum gates with randomly coupled multiparticle environments}, + Journal = jmo, + Year = {2007}, + Doi = {10.1080/09500340701639615}, + Pages = {2339}, + Volume = {54}, +} + + +@article{GraceJPB2007, + Author = {Grace, Matthew and Brif, Constantin and Rabitz, Herschel and Walmsley, Ian A and Kosut, Robert L and Lidar, Daniel A}, + Title = {Optimal control of quantum gates and suppression of decoherence in a system of interacting two-level particles}, + Journal = jpb, + Year = {2007}, + Doi = {10.1088/0953-4075/40/9/s06}, + Pages = {S103}, + Volume = {40}, +} + + +@article{GrondPRA2009b, + Author = {Grond, Julian and von Winckel, Gregory and Schmiedmayer, J\"org and Hohenester, Ulrich}, + Title = {Optimal control of number squeezing in trapped {Bose-Einstein} condensates}, + Journal = pra, + Year = {2009}, + Doi = {10.1103/PhysRevA.80.053625}, + Pages = {053625}, + Volume = {80}, +} + + +@article{GrondPRA2009a, + Author = {Grond, Julian and Schmiedmayer, J\"org and Hohenester, Ulrich}, + Title = {Optimizing number squeezing when splitting a mesoscopic condensate}, + Journal = pra, + Year = {2009}, + Doi = {10.1103/PhysRevA.79.021603}, + Pages = {021603}, + Volume = {79}, +} + diff --git a/docs/src/syntax.md b/docs/src/syntax.md index 094e34a..96b64eb 100644 --- a/docs/src/syntax.md +++ b/docs/src/syntax.md @@ -19,7 +19,7 @@ In `[…](@cite)`, the following variations can be used instead of `@cite`: Lastly, capitalizing the `c` in `@citet` or `@citet*` ensures that the first letter of the citation is capitalized so that it can be used at the beginning of a sentence, e.g., [WinckelIP2008](@Citet) (`[WinckelIP2008](@Citet)`) versus [WinckelIP2008](@citet) (`[WinckelIP2008](@citet)`). -The [natbib](https://mirrors.rit.edu/CTAN/macros/latex/contrib/natbib/natnotes.pdf) commands `@citealt`, `@citealp`, and `@citenum` commands are also recognized. They are not supported by any of the built-in style, but may be handled by [custom styles](@ref customization). +The [natbib](https://mirrors.rit.edu/CTAN/macros/latex/contrib/natbib/natnotes.pdf) commands `@citealt`, `@citealp`, and `@citenum` commands are also recognized. They are not supported by any of the built-in styles, but may be handled by [custom styles](@ref customization). See the [Citation Style Gallery](@ref gallery) for examples of all the possible combinations. @@ -82,7 +82,7 @@ Renders a bibliography for *all* references included in the `.bib` file, not jus ### [Multiple `@bibliography` blocks](@id canonical) -It is possible to have multiple `@bibliography` blocks. However, there can only be one "canonical" bibliography target for any citation (the location where a citation links to). Any `@bibliography` block will automatically skip entries that have already been rendered in an earlier `@bibliography` block. Thus, for two consecutive +It is possible to have multiple `@bibliography` blocks. However, there can only be one "canonical" bibliography target for any citation (the location where a citation links to). Any `@bibliography` block will automatically skip entries that have already been rendered in an earlier canonical `@bibliography` block. Thus, for two consecutive ~~~markdown ```@bibliography diff --git a/src/DocumenterCitations.jl b/src/DocumenterCitations.jl index 4e7093d..a4e607d 100644 --- a/src/DocumenterCitations.jl +++ b/src/DocumenterCitations.jl @@ -10,8 +10,7 @@ using Documenter.Expanders using Documenter.Writers.HTMLWriter using Markdown -using Bibliography -using Bibliography: xyear, xlink, xtitle +using Bibliography: Bibliography, xyear, xlink, xtitle using OrderedCollections: OrderedDict, OrderedSet using Unicode @@ -79,8 +78,12 @@ function CitationBibliography(bibfile::AbstractString=""; style=nothing, cached= # The message is only to transition users through the breaking change # in 1.0. It can be removed in any future 1.1 release. style = :numeric + @debug "Using default style=$(repr(style))" + elseif style == :alpha + @debug "Auto-upgrading :alpha to AlphaStyle()" + style = AlphaStyle() end - entries = import_bibtex(bibfile) + entries = Bibliography.import_bibtex(bibfile) if length(bibfile) > 0 if !isfile(bibfile) error("bibfile $bibfile does not exist") @@ -115,6 +118,36 @@ in its docstring. """ struct Example end + +""" +"Smart" alphabetic citation style (relative to the "dumb" `:alpha`). + +```julia +style = AlphaStyle() +``` + +instantiates a style for [`CitationBibliography`](@ref) that avoids duplicate +labels. Any of the entries that would result in the same label will be +disambiguated by appending the suffix "a", "b", etc. + +Any bibliography that cites a subset of the given `entries` is guaranteed to +have unique labels. +""" +struct AlphaStyle + + # BibTeX key (entry.id) => rendered label, e.g. "GraceJMO2007" => "GBR+07b" + label_for_key::Dict{String,String} + # The internal field `label_for_key` is set by `init_bibliography!` at the + # beginning of the `ExpandBibliography` pipeline step. + + AlphaStyle() = new(Dict{String,String}()) + +end + + +Base.show(io::IO, ::AlphaStyle) = print(io, "AlphaStyle()") + + include("citations.jl") include("bibliography.jl") include("formatting.jl") diff --git a/src/bibliography.jl b/src/bibliography.jl index bc65238..6bc64a4 100644 --- a/src/bibliography.jl +++ b/src/bibliography.jl @@ -6,15 +6,20 @@ Each bibliography is rendered into HTML as a a [definition list](https://www.w3schools.com/tags/tag_dl.asp), a [bullet list](https://www.w3schools.com/tags/tag_ul.asp), or an [enumeration](https://www.w3schools.com/tags/tag_ol.asp) depending on -[`bib_html_list_style`](@ref). For a definition list, the label for each list -item is rendered via [`format_bibliography_label`](@ref) and the full -bibliographic reference is rendered via -[`format_bibliography_reference`](@ref). For bullet lists or enumerations, -[`format_bibliography_label`](@ref) is not used and -[`format_bibliography_reference`](@ref) fully determines the entry. +[`bib_html_list_style`](@ref). + +For a definition list, the label for each list item is rendered via +[`format_bibliography_label`](@ref) and the full bibliographic reference is +rendered via [`format_bibliography_reference`](@ref). + +For bullet lists or enumerations, [`format_bibliography_label`](@ref) is not +used and [`format_bibliography_reference`](@ref) fully determines the entry. The order of the entries in the bibliography is determined by the [`bib_sorting`](@ref) method for the chosen citation style. + +The `ExpandBibliography` step runs [`init_bibliography!`](@ref) before +expanding the first `@bibliography` block. """ abstract type ExpandBibliography <: Builder.DocumentPipeline end @@ -23,6 +28,9 @@ Selectors.order(::Type{ExpandBibliography}) = 2.12 # after CollectCitations function Selectors.runner(::Type{ExpandBibliography}, doc::Documents.Document) Documenter.Builder.is_doctest_only(doc, "ExpandBibliography") && return @info "ExpandBibliography: expanding `@bibliography` blocks." + bib = doc.plugins[CitationBibliography] + style = bib.style # so that we can dispatch on different styles + init_bibliography!(style, bib) for src in keys(doc.blueprint.pages) page = doc.blueprint.pages[src] empty!(page.globals.meta) @@ -34,6 +42,92 @@ function Selectors.runner(::Type{ExpandBibliography}, doc::Documents.Document) end end + +"""Initialize any internal state for rendering the bibliography. + +```julia +init_bibliography!(style, bib) +``` + +is called at the beginning of the [`ExpandBibliography`](@ref) pipeline step. +It may mutate internal fields of `style` or `bib` to prepare for the rendering +of bibliography blocks. + +For the default style, this does nothing. + +For, e.g., [`AlphaStyle`](@ref), the call to `init_bibliography!` determines +the citation labels, by generating unique suffixed labels for all the entries +in the underlying `.bib` file (`bib.entries`), and storing the result in an +internal attribute of the `style` object. + +Custom styles may implement a new method for `init_bibliography!` for similar +purposes. It can be assumed that all the internal fields of the +[`CitationBibliography`](@ref) `bib` object are up-to-date according to +the citations seen by the earlier [`CollectCitations`](@ref) step. +""" +function init_bibliography!(style, bib) end # no-op for default style(s) + +function init_bibliography!(style::Symbol, bib) + init_bibliography!(Val(style), bib) +end + + +function init_bibliography!(style::AlphaStyle, bib) + + # We determine the keys from all the entries in the .bib file + # (`bib.entries`), not just the cited ones (`bib.citations`). This keeps + # the rendered labels more stable, e.g. if you have one `.bib` file across + # multiple related projects. Besides, `bib.citations` isn't guaranteed to + # be complete at the point where `init_bibliography!` is called, since + # bibliography blocks can also introduce new citations (e.g., if using `*`) + entries = OrderedDict{String,Bibliography.Entry}( + # best not to mutate bib.entries, so we'll create a copy before sorting + key => entry for (key, entry) in bib.entries + ) + Bibliography.sort_bibliography!(entries, :nyt) + + # pass 1 - collect dumb labels, identify duplicates + keys_for_label = Dict{String,Vector{String}}() + for (key, entry) in entries + label = alpha_label(entry) # dumb label (no suffix) + if label in keys(keys_for_label) + push!(keys_for_label[label], key) + else + keys_for_label[label] = String[key,] + end + end + + # pass 2 - disambiguate duplicates (append suffix to dumb labels) + label_for_key = style.label_for_key # for in-place mutation + for (key, entry) in entries + label = alpha_label(entry) + if length(keys_for_label[label]) > 1 + i = findfirst(isequal(key), keys_for_label[label]) + label *= _alpha_suffix(i) + end + label_for_key[key] = label + end + @debug "init_bibliography!(style::AlphaStyle, bib)" keys_for_label label_for_key + + # `style.label_for_key` is now up-to-date + +end + + +function _alpha_suffix(i) + if i <= 25 + return string(Char(96 + i)) # 1 -> "a", 2 -> "b", etc. + else + # 26 -> "za", 27 -> "zb", etc. + # I couldn't find any information on (and I was too lazy to test) how + # LaTeX handles disambiguation of more than 25 identical labels, but + # this seems sensible. But also: Seriously? I don't think we'll ever + # run into this in real life. + return "z" * _alpha_suffix(i - 25) + end +end + + abstract type BibliographyBlock <: Selectors.AbstractSelector end @@ -74,11 +168,17 @@ function format_bibliography_reference(::Val{:authoryear}, entry) return "$authors ($year). $title. $(linkify(published_in, link))." end + function format_bibliography_reference(::Val{:alpha}, entry) return format_bibliography_reference(:numeric, entry) end +function format_bibliography_reference(::AlphaStyle, entry) + return format_bibliography_reference(:numeric, entry) +end + + """Format the label for an entry in a `@bibliography` block. ```julia @@ -125,6 +225,20 @@ function format_bibliography_label( end +function format_bibliography_label( + alpha::AlphaStyle, + entry, + citations::OrderedDict{String,Int64} +) + try + return "[$(alpha.label_for_key[entry.id])]" + catch + @error "No AlphaStyle label for $(entry.id). Was `init_bibliography!` called?" alpha.label_for_key + rethrow() + end +end + + """Identify the type of HTML list associated with a bibliographic style. ```julia @@ -143,6 +257,7 @@ bib_html_list_style(style::Symbol) = bib_html_list_style(Val(style)) bib_html_list_style(::Val{:numeric}) = :dl bib_html_list_style(::Val{:authoryear}) = :ul bib_html_list_style(::Val{:alpha}) = :dl +bib_html_list_style(::AlphaStyle) = :dl """Identify the sorting associated with a bibliographic style. @@ -159,6 +274,7 @@ bib_sorting(style::Symbol) = bib_sorting(Val(style)) bib_sorting(::Val{:numeric}) = :citation bib_sorting(::Val{:authoryear}) = :nyt bib_sorting(::Val{:alpha}) = :nyt +bib_sorting(::AlphaStyle) = :nyt function parse_bibliography_block(block, doc, page) @@ -215,6 +331,11 @@ function Selectors.runner(::Type{BibliographyBlock}, x, page, doc) # Local styles are for the Gallery in the documentation only. @assert fields[:Style] isa Symbol style = fields[:Style] + if style == :alpha + # same automatic upgrade as in CitationsBibliography + style = AlphaStyle() + init_bibliography!(style, bib) + end @debug "Overriding local style with $repr($style)" end diff --git a/src/citations.jl b/src/citations.jl index 97ed5a6..00bb5df 100644 --- a/src/citations.jl +++ b/src/citations.jl @@ -320,7 +320,7 @@ end function format_citation( - style::Val{:alpha}, + style::Union{Val{:alpha},AlphaStyle}, entry, citations; # OrderedDict{String,Int64} note::Union{Nothing,String}=nothing, @@ -328,10 +328,17 @@ function format_citation( capitalize::Bool=false, starred::Bool=false ) + if style == Val(:alpha) + # dumb style + label = alpha_label(entry) + else + # smart style + label = style.label_for_key[entry.id] + end if isnothing(note) - link_text = "[$(alpha_label(entry))]" + link_text = "[$label]" else - link_text = "[$(alpha_label(entry)), $note]" + link_text = "[$label, $note]" end if cite_cmd ∈ [:citealt, :citealp, :citenum] @warn "$cite_cmd citations are not supported in the default styles." diff --git a/src/formatting.jl b/src/formatting.jl index 2bd6048..85a0afc 100644 --- a/src/formatting.jl +++ b/src/formatting.jl @@ -81,18 +81,37 @@ function alpha_label(entry) name = Unicode.normalize(entry.authors[1].last; stripmark=true) return uppercasefirst(first(name, 3)) * year else - letters = join( - [ - uppercase(Unicode.normalize(name.last; stripmark=true)[1]) for - name in first(entry.authors, 3) - ], - "", - ) - if length(entry.authors) > 3 - letters *= "+" + letters = [_alpha_initial(name) for name in first(entry.authors, 4)] + if length(entry.authors) > 4 + letters = [first(letters, 3)..., "+"] + end + return join(letters, "") * year + end +end + + +function _is_others(name) + # Support "and others", or "and contributors" directly in the BibTeX file + # (often used for citing software projects) + return ( + (name.last in ["others", "contributors"]) && + (name.first == name.middle == name.particle == name.junior == "") + ) +end + + +function _alpha_initial(name) + # Initial of the last name, but including the "particle" (e.g., "von") + # Used for `alpha_label` + if _is_others(name) + letter = "+" + else + letter = uppercase(Unicode.normalize(name.last; stripmark=true)[1]) + if length(name.particle) > 0 + letter = Unicode.normalize(name.particle; stripmark=true)[1] * letter end - return letters * year end + return letter end diff --git a/test/Project.toml b/test/Project.toml index 8ff0ce8..78815c3 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -7,6 +7,7 @@ JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589" LocalCoverage = "5f6e1e16-694c-5876-87ef-16b5274f298e" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" +OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" diff --git a/test/runtests.jl b/test/runtests.jl index a7bcb69..5552723 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -20,6 +20,11 @@ using SafeTestsets include("test_parse_citation_link.jl") end + print("\n* smart alpha style (test_alphastyle.jl):") + @time @safetestset "smart alpha style" begin + include("test_alphastyle.jl") + end + print("\n* integration test (test_makedocs.jl):") @time @safetestset "makedocs" begin include("test_makedocs.jl") diff --git a/test/test_alphastyle.jl b/test/test_alphastyle.jl new file mode 100644 index 0000000..6de8002 --- /dev/null +++ b/test/test_alphastyle.jl @@ -0,0 +1,48 @@ +using OrderedCollections: OrderedDict +import DocumenterCitations: + _alpha_suffix, + format_citation, + format_bibliography_label, + init_bibliography!, + AlphaStyle, + CitationBibliography + + +@testset "alpha suffix" begin + @test _alpha_suffix(1) == "a" + @test _alpha_suffix(2) == "b" + @test _alpha_suffix(25) == "y" + @test _alpha_suffix(26) == "za" + @test _alpha_suffix(27) == "zb" + @test _alpha_suffix(50) == "zy" + @test _alpha_suffix(51) == "zza" +end + + +@testset "alpha label disambiguation" begin + + bib = CitationBibliography(joinpath(@__DIR__, "..", "docs", "src", "refs.bib"),) + _c = OrderedDict{String,Int64}() # dummy "citations" + dumb = Val(:alpha) + smart = AlphaStyle() + init_bibliography!(smart, bib) + + lbl1 = format_citation(dumb, bib.entries["GraceJPB2007"], _c) + lbl2 = format_citation(dumb, bib.entries["GraceJMO2007"], _c) + @test lbl1 == lbl2 == "[GBR+07]" + + lbl1 = format_bibliography_label(dumb, bib.entries["GraceJPB2007"], _c) + lbl2 = format_bibliography_label(dumb, bib.entries["GraceJMO2007"], _c) + @test lbl1 == lbl2 == "[GBR+07]" + + lbl1 = format_citation(smart, bib.entries["GraceJPB2007"], _c) + lbl2 = format_citation(smart, bib.entries["GraceJMO2007"], _c) + @test lbl1 == "[GBR+07a]" + @test lbl2 == "[GBR+07b]" + + lbl1 = format_bibliography_label(smart, bib.entries["GraceJPB2007"], _c) + lbl2 = format_bibliography_label(smart, bib.entries["GraceJMO2007"], _c) + @test lbl1 == "[GBR+07a]" + @test lbl2 == "[GBR+07b]" + +end diff --git a/test/test_formatting.jl b/test/test_formatting.jl index 58f3a67..b8f2e2e 100644 --- a/test/test_formatting.jl +++ b/test/test_formatting.jl @@ -35,6 +35,9 @@ end @test alpha_label(bib.entries["Tannor2007"]) == "Tan07" @test alpha_label(bib.entries["FuerstNJP2014"]) == "FGP+14" @test alpha_label(bib.entries["ImamogluPRE2015"]) == "IW15" - @test alpha_label(bib.entries["SciPy"]) == "JOP+01" + @test alpha_label(bib.entries["SciPy"]) == "JOP+01" # _is_others @test alpha_label(bib.entries["MATLAB:2014"]) == "MAT14" + @test alpha_label(bib.entries["LapertPRA09"]) == "LTTS09" + @test alpha_label(bib.entries["GrondPRA2009b"]) == "GvWSH09" + @test alpha_label(bib.entries["WinckelIP2008"]) == "vWB08" end