Closed denismaier closed 2 years ago
Agreed. I already have some of the basic setups in place for this functionality:
% Parenthetical citations should be rendered exactly like inline citations,
% but the whole citation should be surrounded by parentheses,
% and brackets should be used where parentheses are within the citation
% (e.g., journal years and publication and institution blocks)
\definebtx
[sbl:\s!cite:paren]
[sbl:\s!cite:inline]
[\c!left={\startparen},
\c!right={\stopparen}]
% Citations in footnotes should be rendered exactly like citations in the text,
% but followed by a period and placed in footnotes
\definebtx
[sbl:\s!cite:footnote]
[sbl:\s!cite:inline]
[\c!left={\startfootnote},
\c!right={.\stopfootnote}]
% Citations in endnotes (for the philistines who use them) should be rendered exactly like citations in the text,
% but followed by a period and placed in endnotes
\definebtx
[sbl:\s!cite:endnote]
[sbl:\s!cite:inline]
[\c!left={\startendnote},
\c!right={.\stopendnote}]
Once autocite
is defined, these variants should be accessible through commands like \autocite[alternative=paren][...]
and \autocite[alternative=footnote][...]
, but wrapping this in \parencite
, \footcite
, and \endcite
would be a lot less verbose.
I wonder if these commands should be added in the core?
That might make sense for \parencite
, \footcite
, and \endcite
(although \parencite
might be redundant for specs like Chicago and APA, which put parentheses around their in-text citations already). I'm not sure about \autocite
, though. This one is only needed for SBL because we need to invoke \cite
(add entry to list) or \textcite
(don't add entry to list) conditionally.
Re \autocite: I'd like to think about this as a high-level wrapper command. Say you switch from SBL to APA, autocite
would then switch to \parencite
in APA while it would mean \footcite
in SBL. In addition, it could also do some things like moving punctuation around and similar things. (At least, that's how it works in biblatex.)
Yes, that makes sense. The default alternative
setting for citations already facilitates this:
% General rules to be inherited or excepted by other types of citations
\definebtx
[sbl:\s!cite]
[sbl]
[\c!alternative=footnote, % by default, SBL uses footnote citations (defined below)
...
]
(Note that I changed this default from inline
; I'll push this change in my next commit.)
Compare this with publ-imp-chicago.mkvi
:
\definebtx
[chicago:\s!cite]
[chicago]
[\c!alternative=authoryear,
...
]
This also grants us flexibility, since a user can manually change the default alternative to something else via
\setupbtx[sbl:cite][alternative=inline]
I might do something like this, as defaulting to the inline
alternative makes it easy for me to cite sources with non-citation text in a footnote without having to cram the other text in the lefttext
and righttext
arguments.
Yes, that makes sense. The default
alternative
setting for citations already facilitates this:
Ok, I see. So the question is not switching the defaults. The addition would be a way to move punctuation around.
Right. I'm not sure how biblatex
handles punctuation around a citation intelligently. For the inline
alternative and the alternative derived from it, I currently print the citation without any trailing punctuation. But if I move some lines of code around, I could allow users to provide their preferred punctuation in the righttext
field, as this should be set before the right
field.
Ok, here is what biblatex does:
\cite
prints a plain citation command, no punctuation around it. So in authortitle styles this is "Doe, Title, p. 56", in authoryear styles this will be "Doe, 2020, p. 56".\parencite
puts the citation in parentheses: e.g. "(Doe, Title, p. 56)" or "(Doe, 2020, p. 56)" .\footcite
puts the citation in a footnote and adds a period at the end: "\footnote{Doe, Title, p. 56.}" or "\footnote{Doe, 2020, p. 56.}"autocite
acts as another high-level wrapper: in authoryear styles and in some authortitle styles this will be equivalent to \parencite
, in the verbose style family this will be \footcite
--- or actually \smartcite
, which acts as \footcite
when called in the body text, and as \parencite
when called in a footnote...So far so good, we should already be able to achieve this with present ConTeXt features:
\definebtx
[sbl:\s!cite]
[sbl]
[\c!alternative=footnote, % by default, SBL uses footnote citations (defined below)
...
]
\definebtx
[sbl:\s!cite:inline]
[sbl:\s!cite]
\definebtx
[sbl:\s!cite:footnote]
[sbl:\s!cite:inline]
[\c!left={\startfootnote},
\c!right={.\stopfootnote}]
\definebtx
[sbl:\s!cite:paren]
[sbl:\s!cite:inline]
[\c!left={\startparen},
\c!right={\stopparen}]
Now, what biblatex's \autocite
does regarding punctuation is this: Say you write a book for a publisher that uses APA, so you write like this:
This is a sentence with a citation at the end \cite[doe].
Default citation alternative is defined as paren
so you get parenthetical citations.
Unfortunately, your manuscript is rejected and you decide to submit it to SBL this time. So you switch to SBL where footnotes appear in footnotes. The default citation format is now "footnote" so your mostly done. But, given the example from above you'll end up with this:
This is a sentence with a citation at the end \footnote{\cite[alternative=inline][doe].}.
So, as you see, the citation is now inproperly placed, and you'll have to edit the manuscript.
And here, biblatex's \autocite
game changing feature comes into play: This command will scan for punctation coming after the command and shift that around. So, the above example will come out as this:
This is a sentence with a citation at the end.\footnote{\cite[alternative=inline][doe].}
So, footnote after the period and spaces collapsed. This makes it really easy to write in a style agnostic way, and to adapt your output to a given style without editing the text.
In the interest of clarity, I couldn't get
[sbl:\s!cite:footnote]
[sbl:\s!cite:inline]
[\c!left={\startfootnote},
\c!right={.\stopfootnote}]
and
[sbl:\s!cite:endnote]
[sbl:\s!cite:inline]
[\c!left={\startendnote},
\c!right={.\stopendnote}]
to work. I ended up following the pattern in publ-imp-cite.mkvi
, but overriding the style:
% Inline footnote citation setup
\startsetups btx:sbl:cite:footnote
\startfootnote
\fastsetup{btx:sbl:cite:inline}
\btxperiod
\stopfootnote
\stopsetups
% Inline endnote citation setup
\startsetups btx:sbl:cite:endnote
\startendnote
\fastsetup{btx:sbl:cite:inline}
\btxperiod
\stopendnote
\stopsetups
Since an endnote citation variant isn't currently provided in publ-imp-cite.mkvi
, I added one for general usage:
\startsetups \s!btx:\s!cite:endnote
\startendnote
\fastsetup{btx:cite:entry}
\stopendnote
\stopsetups
As for intelligent punctuation placement, I think that it may be possible using the \doifnextcharelse
macro, but I'm not sure. As it is, not all journals place footnotes after punctuation (the Spanish journal Filología Neotestamentaria requires footnotes to come before punctuation; as an American, I'm not sure if this reflects a more general European tendency, but it's something I've encountered), so relocating the punctuation automatically may not always produced the desired result. Hans has said that he can put together something in Lua that will reorder punctuation and quotations to fit American style, so he may also be able to incorporate this feature into that effort.
Re: Intelligent punctuation: this is totally language specific. I've seen the Spanish method of footnote marker placement also in French. A good place to start reading about this is the manual for csquotes
latex package.
Thanks for the pointer! I think I can see how to make this work, at least in a rudimentary way. Here's some code for \footcite
:
% footnote citation
\unexpanded\def\footcite{\dodoubleempty\dofootcite}
\unexpanded\def\dofootcite[#1][#2]#3#4{%
\doifinstring{#4}{,.;!?}{#4}%
\startfootnote
\ifsecondargument
% if there are two optional arguments,
% then assume the first is the prenote and the second is the postnote
% If this entry has already been cited, then do a short citation
\btxdoifcombiinlistelse{\currentbtxdataset}{#3} {
\textcite[alternative=short,lefttext={#1},righttext={#2}][#3]%
} {
\cite[lefttext={#1},righttext={#2}][#3]%
}
\else
\iffirstargument
% if there is only one optional argument,
% then assume it is a postnote
\btxdoifcombiinlistelse{\currentbtxdataset}{#3} {
\textcite[alternative=short,righttext={#1}][#3]%
} {
\cite[righttext={#1}][#3]%
}
\else
% if there are no optional arguments,
% then just cite the entry by itself
\btxdoifcombiinlistelse{\currentbtxdataset}{#3} {
\textcite[alternative=short][#3]%
} {
\cite[#3]%
}
\fi
\fi
\stopfootnote
\doifnotinstring{#4}{,.;!?}{#4}%
}
(Note that the \btxdoifcombiinlistelse{\currentbtxdataset}{#3}
calls are intended to check if the entry tag in argument 3 has been marked as cited yet, but they don't work at the moment; I've e-mailed Hans about this, as he had mentioned in a previous e-mail that a new helper may need to be added to the publications support module to make this work.)
The fourth argument is not expected to be passed as a proper argument in brackets; rather, it is a placeholder for the character following the macro and its other arguments.
The set of characters we want to handle in this way is specified as a string—specifically, as the second argument of the \doifinstring
and \doifnotinstring
macros. Obviously, this set is language-dependent; for American style, it would include most punctuation, while for the French and Spanish placement mentioned above, the set would be empty. So we would want this set to be customizable. We could probably use autopunctuation
(which is already set up for multiple language definitions in mult-def.lua
) as an option to \definebtx[sbl:\s!cite]
for this purpose.
Actually, on closer examination, this only seems to be working for the period, comma, and semicolon characters. Are the exclamation point and question mark protected somehow?
This is really confusing. The following MWE that uses the same code works perfectly fine:
\def\autopuncttest#1#2{%
\removeunwantedspaces%
\doifinstring{#2}{,.!?;:}{#2}%
\footnote{#1}%
\doifnotinstring{#2}{,.!?;:}{#2}%
}
\starttext
This works \autopuncttest{Footnote 1}, as does this \autopuncttest{Footnote 2}; and so does this \autopuncttest{Footnote 3}.\blank
But this one fails if defined in the bib spec \autopuncttest{Footnote 4}!\blank
And why does this one fail in the bib spec too \autopuncttest{Footnote 5}?\blank
\stoptext
But this MWE reproduces the error:
\startbtxrenderingdefinitions[myspec]
\def\autopuncttest#1#2{%
\removeunwantedspaces%
\doifinstring{#2}{,.!?;:}{#2}%
\footnote{#1}%
\doifnotinstring{#2}{,.!?;:}{#2}%
}
\stopbtxrenderingdefinitions
\usebtxdefinitions[myspec]
\starttext
This works \autopuncttest{Footnote 1}, as does this \autopuncttest{Footnote 2}; and so does this \autopuncttest{Footnote 3}.\blank
But this one fails if defined in the bib spec \autopuncttest{Footnote 4}!\blank
And why does this one fail in the bib spec too \autopuncttest{Footnote 5}?\blank
\stoptext
So the problem seems to be something going on in the btxrenderingdefinitions
environment. I've sent an e-mail about this to the mailing list.
All right, Hans has found a solution! More details can be found on the mailing list thread "Unusual error with \doifinstring in \startbtxrenderingdefinitions ... \stopbtxrenderingdefinitions". But to keep things short, temporarily reverting to the default ConTeXt catcode table (which governs which characters to treat as special in buffers) makes things work:
\startbtxrenderingdefinitions[myspec]
\pushcatcodetable
\setcatcodetable\ctxcatcodes
\def\autopuncttest#1#2{%
\removeunwantedspaces%
\doifinstring{#2}{,.!?;:}{#2}%
\footnote{#1}%
\doifnotinstring{#2}{,.!?;:}{#2}%
}
\popcatcodetable
\stopbtxrenderingdefinitions
\usebtxdefinitions[myspec]
\starttext
This works \autopuncttest{Footnote 1}, as does this \autopuncttest{Footnote 2}; and so does this \autopuncttest{Footnote 3}.\blank
Now, even this one works \autopuncttest{Footnote 4}!\blank
And can you believe that this one does, too \autopuncttest{Footnote 5}?\blank
\stoptext
As I've worked through this some more, I've found that the easiest way to work within the btx namespace is to set default parameter values within the namespace when we initially setup the btx rendering and then temporarily reset them for individual citations as options to the \cite
, \textcite
, etc. commands:
\definebtx
[sbl]
[\c!default=default,
\c!specification=sbl,
\c!otherstext={\btxspace\btxlabeltext{others}},
% TODO: the following fields are not language-sensitive and probably should be
useibid=\v!no, % useibid=yes will enable abbreviation of consecutive citations of the same entry as "ibid."
useidem=\v!no, % useidem=yes will enable abbreviation of an author cited multiple times consecutively as "idem"
synonym=\v!abbreviation, % TODO: this can determine which synonyms set we will use for shorthands
synonyms=\v!abbreviations, % TODO: this can determine which synonyms set we will use for shorthands
%\c!journalconversion=\v!normal,
\c!monthconversion=\v!month,
\c!stopper:initials={. }, % with a (breakable) space
\c!separator:names:2={\btxcomma}, % between subsequent names except for last 2
\c!separator:names:3={\btxcomma\btxlabeltext{and}\space}, % between last 2 names if > 2
\c!separator:names:4={\btxspace\btxlabeltext{and}\space}] % between only 2 names
% Because some common punctuation characters are escaped in publ-ini.mkiv,
% we have to set this option separately
\pushcatcodetable
\setcatcodetable\ctxcatcodes
\setupbtx[sbl][autopunctuation={!,.:;?}]% undelimited list of trailing punctuation to move before footnote citations
\popcatcodetable
...
% General rules to be inherited or excepted by other types of citations
\definebtx
[sbl:\s!cite]
[sbl]
[\c!alternative=footnote, % by default, SBL uses footnote citation format (defined below)
\c!otherstext={\btxspace\btxlabeltext{others}}, % use et al. for truncated author / editor list
\c!etallimit=3, % don't use et al. for 3 or fewer authors
\c!etaldisplay=1, % but if there are > 3, then only list the first explicitly
\c!etaloption=last,
\c!authorconversion=\v!normal, % by default, use normal name order for in-text citations
% TODO: the following fields are not language-sensitive and probably should be
printauthor=\v!yes, % printauthor=no will skip printing the author
printpubl=\v!yes, % printpubl=no will skip printing the publication fields
lefttext=, % empty by default
righttext=, % empty by default
punct=, % trailing punctuation (empty by default)
\c!sorttype=normal, % \v!normal ?
\c!style=, % do not apply any styles across the whole citation
\c!compress=\v!yes] % note that cite sorts only work with compress=yes.
...
% Inline footnote citation setup (with intelligent trailing punctuation replacement)
\startsetups btx:sbl:cite:footnote
\removeunwantedspaces
\doifinstring{\btxparameter{punct}}{\btxparameter{autopunctuation}}{\btxparameter{punct}}
\startfootnote
\fastsetup{btx:sbl:cite:inline}
\removeunwantedspaces
\removepunctuation
\btxperiod
\stopfootnote
\doifnotinstring{\btxparameter{punct}}{\btxparameter{autopunctuation}}{\btxparameter{punct}}
\stopsetups
...
% Define more concise biblatex-style citation commands
% default alternative citation
\def\autocite{\dodoubleempty\doautocite}
\def\doautocite[#1][#2]#3#4{%
\ifsecondargument
% if there are two optional arguments,
% then assume the first is the prenote and the second is the postnote
\cite[lefttext={#1},righttext={#2},punct={#4}][#3]%
\else
\iffirstargument
% if there is only one optional argument,
% then assume it is a postnote
\cite[righttext={#1},punct={#4}][#3]%
\else
% if there are no optional arguments,
% then just cite the entry by itself
\cite[punct={#4}][#3]%
\fi
\fi
}
% inline citation (biblatex uses \textcite, but that is reserved for a different function in publ-ini.mkiv)
\def\inlinecite{\dodoubleempty\doinlinecite}
\def\doinlinecite[#1][#2]#3#4{%
\ifsecondargument
% if there are two optional arguments,
% then assume the first is the prenote and the second is the postnote
\cite[alternative=inline,lefttext={#1},righttext={#2},punct={#4}][#3]%
\else
\iffirstargument
% if there is only one optional argument,
% then assume it is a postnote
\cite[alternative=inline,righttext={#1},punct={#4}][#3]%
\else
% if there are no optional arguments,
% then just cite the entry by itself
\cite[alternative=inline,punct={#4}][#3]%
\fi
\fi
}
% parenthetical citation
\unexpanded\def\parencite{\dodoubleempty\doparencite}
\unexpanded\def\doparencite[#1][#2]#3#4{%
\ifsecondargument
% if there are two optional arguments,
% then assume the first is the prenote and the second is the postnote
\cite[alternative=paren,lefttext={#1},righttext={#2},punct={#4}][#3]%
\else
\iffirstargument
% if there is only one optional argument,
% then assume it is a postnote
\cite[alternative=paren,righttext={#1},punct={#4}][#3]%
\else
% if there are no optional arguments,
% then just cite the entry by itself
\cite[alternative=paren,punct={#4}][#3]%
\fi
\fi
}
% footnote citation
\def\footcite{\dodoubleempty\dofootcite}
\def\dofootcite[#1][#2]#3#4{%
\ifsecondargument
% if there are two optional arguments,
% then assume the first is the prenote and the second is the postnote
\cite[alternative=footnote,lefttext={#1},righttext={#2},punct={#4}][#3]%
\else
\iffirstargument
% if there is only one optional argument,
% then assume it is a postnote
\cite[alternative=footnote,righttext={#1},punct={#4}][#3]%
\else
% if there are no optional arguments,
% then just cite the entry by itself
\cite[alternative=footnote,punct={#4}][#3]%
\fi
\fi
}
Hans has also defined the following helper function for checking if a given entry has already been cited:
\startluacode
local ctx_doifelse = commands.doifelse
interfaces.implement { -- shared with mkiv so no public
name = "btxdoifelsecitedone",
protected = true,
-- public = true,
-- arguments = "2 arguments",
arguments = "2 strings",
actions = function(dataset,tag)
-- dataset ignored
local list = structures.lists.tobesaved
local done = false
for i=1,#list do
local u = list[i].userdata
if u and u.btxref == tag then
done = true
break
end
end
ctx_doifelse(done)
end
}
\stopluacode
\unprotect
\protected\def\btxdoifelsecitedone#1#2%
{\clf_btxdoifelsecitedone{#1}{#2}}
\protect
This is now used in the btx:sbl:cite:inline
setup to determine whether a long or short citation should be used:
% Check if the entry with the current tag has been cited already
\btxdoifelsecitedone\currentbtxdataset\currentbtxtag{
% If it has, then use a short citation
\fastsetup{btx:sbl:cite:short}
} {
...
% Otherwise, invoke the appropriate setup
\fastsetup{btx:sbl:inline:\currentbtxcategory}
}
I've changed publ-imp-sbl-test.tex
to use \autocite
consecutively for each entry, and the second entry is correctly being rendered in short form. I consider this issue closed.
Your todo list says you'll add an autocite and parencite command. While you're at it, I think footcite would be an obvious candidate for inclusion.