jjmccollum / context-sbl

Society of Biblical Literature (SBL) style files for ConTeXt
2 stars 0 forks source link

Autocite, parencite, footcite #3

Closed denismaier closed 2 years ago

denismaier commented 2 years ago

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.

jjmccollum commented 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.

denismaier commented 2 years ago

I wonder if these commands should be added in the core?

jjmccollum commented 2 years ago

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.

denismaier commented 2 years ago

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.)

jjmccollum commented 2 years ago

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.

denismaier commented 2 years ago

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.

jjmccollum commented 2 years ago

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.

denismaier commented 2 years ago

Ok, here is what biblatex does:

  1. the basic \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".
  2. \parencite puts the citation in parentheses: e.g. "(Doe, Title, p. 56)" or "(Doe, 2020, p. 56)" .
  3. \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.}"
  4. 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.

jjmccollum commented 2 years ago

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.

denismaier commented 2 years ago

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.

jjmccollum commented 2 years ago

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.

jjmccollum commented 2 years ago

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?

jjmccollum commented 2 years ago

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
jjmccollum commented 2 years ago

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.

jjmccollum commented 2 years ago

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
jjmccollum commented 2 years ago

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.