11

I am working on my PhD Thesis, and need to create an index of all primary sources quoted. I have created "cite" commands so that it automatically adds an index line whenever I cite:

\newcommand{\citeherm}[1]{\emph{Shepherd of Hermas}~#1\index{Shepherd of Hermas!#1}}

This works great, except that the index orders numerals incorrectly. '11' will come between '1' and '2', which is clearly incorrect in this case.

I have attached a minimal example. Who can help?

(BTW I have thought that you could do this by hand by using a running zero that does not print, but I wouldn't know how to automate that, in my command above.

\index{Shepherd of Hermas!03.13–18@3.13–8}

)

Minimal example

\documentclass[12pt]{memoir}
\newcommand{\citeherm}[1]{\emph{Shepherd of Hermas}~#1\index{Shepherd of Hermas!#1}}
\newcommand{\citeash}[1]{\emph{Testament of Asher}~#1\index{Testament of Asher!#1}}
\makeindex

\begin{document}

“bla bla bla” \citeherm{12.1}
“bla bla bla” \citeherm{12.2}
“bla bla bla” \citeherm{12.3}
“bla bla bla” \citeherm{12.4}
“bla bla bla” \citeherm{12.10}
“bla bla bla” \citeherm{12.11}
“bla bla bla” \citeherm{12.20}
“bla bla bla” \citeherm{12.21}


“bla bla bla” \citeash{1.1}
“bla bla bla” \citeash{2.1}
“bla bla bla” \citeash{10.1}
\printindex

\end{document}  
Tom de Bruin
  • 1,222
  • Have you considered using \index{order@text} where order determines the order and text the text to display. This way you could either use an additional counter, if you want them sorted in the order of appearance, or add a 0 in front without it being printed. – Roelof Spijker Oct 25 '11 at 10:29

1 Answers1

10

I assume that your references have the form x.y-z, possibly omitting the -z part, where x, y, and z are numbers less than 100:

\documentclass[12pt]{memoir}

\makeatletter
\def\bruin@normalize#1{\@bruin@normalizei#1-\@nil}
\def\@bruin@normalizei#1.#2-#3\@nil{%
    \two@digits{#1}.\two@digits{#2}%
  \if\relax\detokenize{#3}\relax\else
     -\expandafter\two@digits\expandafter{\@gobblehyphen#3}%
  \fi}
\def\@gobblehyphen#1-{#1}
\newcommand{\defcite}[2]{%
  \newcommand{#1}[1]{\emph{#2}~##1\index{#2!\bruin@normalize{##1}@##1}}%
}
\makeatother

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

\makeindex

\begin{document}

bla bla bla \citeherm{12.1}
bla bla bla \citeherm{12.2}
bla bla bla \citeherm{12.3}
bla bla bla \citeherm{12.4}
bla bla bla \citeherm{12.10}
bla bla bla \citeherm{12.11}
bla bla bla \citeherm{12.20}
bla bla bla \citeherm{12.21}


bla bla bla \citeash{1.1}
bla bla bla \citeash{2.1}
bla bla bla \citeash{10.1}
\printindex

\end{document}  

If the format is different, then some corrections have to be made. For example, if the numbers can be three digits long, add

\def\three@digits#1{\ifnum#1<1000 0\ifnum#1<10 0\fi\fi#1}

and use \three@digits instead of \two@digits.

I've also defined a "catch-all" command for defining uniformly the indexing commands.

Added: support for list arguments (and three digits)

\makeatletter
\def\three@digits#1{\ifnum#1<100 0\ifnum#1<10 0\fi\fi#1}
\def\bruin@normalize#1{\@bruin@normalizei#1-\@nil}
\def\@bruin@normalizei#1.#2-#3\@nil{%
    \three@digits{#1}.\three@digits{#2}%
  \if\relax\detokenize{#3}\relax\else
     -\expandafter\three@digits\expandafter{\@gobblehyphen#3}%
  \fi}
\def\@gobblehyphen#1-{#1}
\newcommand{\defcite}[2]{%
  \newcommand{#1}[1]{\emph{#2}\bruin@dolist{#2}{##1}}}
\def\bruin@dolist#1#2{%
  \toks@={~\@gobbletwo}%
  \@for\next:=#2\do{%
    \edef\next{,\noexpand\space\next
      \noexpand\index{#1!\expandafter\bruin@normalize\expandafter{\next}@\next}}%
    \toks@=\expandafter{\the\expandafter\toks@\next}%
  }\the\toks@
}
\makeatother

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

Now an input such as \citeherm{12.1,35.4,28.2} will print

Shepherd of Hermas 12.1, 35.4, 28.2

with a nonbreaking space between "Hermas" and the first number; it will index all three references.

Added: support for chapter ranges

\makeatletter
\def\three@digits#1{\ifnum#1<100 0\ifnum#1<10 0\fi\fi#1}
\def\bruin@normalize#1{\@bruin@normalizei#1-.\@nil}
\def\@bruin@normalizei#1.#2-#3.#4\@nil{%
    \three@digits{#1}.\three@digits{#2}%
  \if\relax\detokenize{#4}\relax
     % same chapter range
     \if\relax\detokenize{#3}\relax
       % simple range
       % already set
     \else
       -\expandafter\three@digits\expandafter{\@gobblehyphen#3}%
     \fi
  \else
     % different chapters range
     -\three@digits{#3}.\expandafter\three@digits\expandafter{\@gobblehyphenperiod#4}%
  \fi}
\def\@gobblehyphen#1-{#1}
\def\@gobblehyphenperiod#1-.{#1}

\newcommand{\defcite}[2]{%
  \newcommand{#1}[1]{\emph{#2}\bruin@dolist{#2}{##1}}}
\def\bruin@dolist#1#2{%
  \toks@={~\@gobbletwo}%
  \@for\next:=#2\do{%
    \edef\next{,\noexpand\space\next
      \noexpand\index{#1!\expandafter\bruin@normalize\expandafter{\next}@\next}}%
    \toks@=\expandafter{\the\expandafter\toks@\next}%
  }\showthe\toks@\the\toks@
}
\makeatother

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

Now also \citeherm{1.2-3.4} will be indexed. Lists will continue to work as before.

Added: support also for simple citations

\makeatletter
\def\three@digits#1{\ifnum#1<100 0\ifnum#1<10 0\fi\fi#1}
\def\bruin@normalize#1{\@bruin@normalizei#1.\@nil}
\def\@bruin@normalizei#1.#2\@nil{%
  \if\relax\detokenize{#2}\relax
     % only one number
     \three@digits{#1}\expandafter\@gobble
  \else
     \expandafter\@firstofone
  \fi{\@bruin@gobbleperiod#1.#2\@nil}
}
\def\@bruin@gobbleperiod#1.\@nil{\@bruin@normalizeii#1-.\@nil}
\def\@bruin@normalizeii#1.#2-#3.#4\@nil{%
    \three@digits{#1}.\three@digits{#2}%
  \if\relax\detokenize{#4}\relax
     % same chapter range
     \if\relax\detokenize{#3}\relax
       % simple range
       % already set
     \else
       -\expandafter\three@digits\expandafter{\@gobblehyphen#3}%
     \fi
  \else
     % different chapters range
     -\three@digits{#3}.\expandafter\three@digits\expandafter{\@gobblehyphenperiod#4}%
  \fi}
\def\@gobblehyphen#1-{#1}
\def\@gobblehyphenperiod#1-.{#1}

\newcommand{\defcite}[2]{%
  \newcommand{#1}[1]{\emph{#2}\bruin@dolist{#2}{##1}}}
\def\bruin@dolist#1#2{%
  \toks@={~\@gobbletwo}%
  \@for\next:=#2\do{%
    \edef\next{,\noexpand\space\next
      \noexpand\index{#1!\expandafter\bruin@normalize\expandafter{\next}@\next}}%
    \toks@=\expandafter{\the\expandafter\toks@\next}%
  }\the\toks@
}

\makeatother

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

This should allow also citations like \citeherm{12}.

Final (?) addition

Here is a "LaTeX3" implementation:

\documentclass[12pt]{memoir}
\usepackage{xparse}
\ExplSyntaxOn

\tl_new:N \l_bruin_argi_tl
\tl_new:N \l_bruin_argii_tl
\tl_new:N \l_bruin_argx_tl
\tl_new:N \l_bruin_temp_tl
\tl_new:N \l_bruin_final_tl

% First of all we split the comma separated list; we store |#1|, the
% title of the book, in |\bruin_argi_tl| and |\bruin_argx_tl| to
% contain |\use_none:nn| that at the end will gobble the first two
% tokens (a comma and a space); then we put the split list of
% arguments followed by |{\NoValue}| after |\bruin_process:n|, because
% it will be used to terminate the recursion.
\NewDocumentCommand\bruin_dolist:nn {m >{\SplitList{,}}m}{
  \tl_set:Nn \l_bruin_argi_tl { #1 }
  \tl_set:Nn \l_bruin_argx_tl { \use_none:nn }
  \bruin_process:n #2 {\NoValue}
}
% |\bruin_process:n| initiates the recursion; it examines the contents
% of the first pair of braces after it and, if different from
% |\NoValue|, stores it into |\bruin_argii_tl|. Then it appends also
% it to the list |\bruin_argx_tl|, after a comma and a space. The same
% contents is also processed by |\bruin_normalize:n|. Then we execute
% |\bruin_do:| and call again |\bruin_process:n| to restart the recursion.
\cs_new:Nn \bruin_process:n {
  \IfNoValueTF{#1}
    {}% end of recursion
    {
     \tl_set:Nn \l_bruin_argii_tl {#1}
     \tl_set:Nx \l_bruin_argx_tl { \l_bruin_argx_tl , \c_space_tl #1 }
     \bruin_normalize:n {#1} \bruin_do: \bruin_process:n
    }
}
% |\bruin_normalize:n| is responsible for splitting an entry of the
% form |A-B| (|A| and |B| will actually be something like |1.2|) into
% the two components, which are passed to |\bruin_normalizei:nn|
\NewDocumentCommand\bruin_normalize:n {>{\SplitArgument{1}{-}}m}{
  \bruin_normalizei:nn #1
}
% If the entry was |A-B|, |\bruin_normalizei:nn| is passed |{A}{B}|,
% otherwise it is passed |{A}{\NoValue}|
\cs_new:Nn \bruin_normalizei:nn {
  \IfNoValueTF{#2}
    {\bruin_three:n {#1}} % simple entry
    {\bruin_three:nn {#1}{#2}} % complex entry
}
% |\bruin_three:n| splits an argument of the form |1.2.3| into the
% components; it's quite similar to |\bruin_normalize:n|
\NewDocumentCommand\bruin_three:n {>{\SplitArgument{2}{.}}m}{
  \bruin_threedo:nnn #1
  \tl_set:Nx \l_bruin_final_tl {\l_bruin_temp_tl}
}
% |\bruin_threedo:nnn| sets |\l_bruin_final_tl| to the components but
% with three digits; |1| is transformed into |001| and |1.2| becomes
% |001.002|, while |1.2.3| becomes |001.002.003|
\cs_new:Nn \bruin_threedo:nnn {
  \IfNoValueTF{#3}
    {\IfNoValueTF{#2}
       {\tl_set:Nx \l_bruin_temp_tl
          {\bruin_threedig:n{#1}}}
       {\tl_set:Nx \l_bruin_temp_tl
          {\bruin_threedig:n{#1}.\bruin_threedig:n{#2}}}
    }
    {\tl_set:Nx \l_bruin_temp_tl
       {\bruin_threedig:n{#1}.\bruin_threedig:n{#2}.\bruin_threedig:n{#3}}
    }
}
% we do similarly for |\bruin_three:nn|, calling |\bruin_threedo:nnn| twice.
\NewDocumentCommand\bruin_three:nn
  {>{\SplitArgument{2}{.}}m >{\SplitArgument{2}{.}}m}
  { \bruin_threedo:nnn #1
    \tl_set:Nx \l_bruin_final_tl {\l_bruin_temp_tl }
    \bruin_threedo:nnn #2
    \tl_set:Nx \l_bruin_final_tl {\l_bruin_final_tl -- \l_bruin_temp_tl}
  }

\cs_new:Nn \bruin_do: {
  \cs_set:Nx \bruin_next: {
    \exp_not:N \index 
      {\l_bruin_argi_tl ! \l_bruin_final_tl \c_space_tl @ \l_bruin_argii_tl }
  }
  \bruin_next: 
}
\cs_new:Nn \bruin_threedig:n {
  \int_compare:nNnTF {#1} < {100}
    { 0 \int_compare:nNnTF {#1} < {10} {0}{} }
    {} #1
}

% |\bruin_defcite:nn| defines |#1| as a macro with one argument, first
% storing its second argument in it. So
%
% |\defcite{\citeT}{Title}|
%
% defines |\citeT| as if it were
%
% |\def\citeT#1{\emph{Title}\space\bruin_dolist:nn{Title}{#1}\l_bruin_argx_tl}|
%
% The token list |\l_bruin_argx_tl| will be built by |\bruin_dolist:nn|
\cs_new:Nn \bruin_defcite:nn {
  \cs_new:Npn #1##1{\emph{#2}\space\bruin_dolist:nn {#2}{##1} \l_bruin_argx_tl }}
\cs_set_eq:NN \defcite \bruin_defcite:nn
\ExplSyntaxOff

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

\makeindex

\begin{document}

%\tracingmacros=1
bla bla bla \citeherm{12.1-2}
\newpage
bla bla bla \citeherm{12.2,13.9}
\newpage
bla bla bla \citeherm{12.3-13.4}
\newpage
bla bla bla \citeherm{12}
\newpage
bla bla bla \citeherm{12.10}
\newpage
bla bla bla \citeherm{12.11}
\newpage
bla bla bla \citeherm{12.20}
\newpage
bla bla bla \citeherm{12.21}
\newpage


bla bla bla \citeash{1.2.3}
\newpage
bla bla bla \citeash{2.1}.
\newpage
bla bla bla \citeash{10.1,1.1,2.9-19}.
\newpage
\printindex

\end{document}

This should support also three levels, as you see in the \citeash example.


Version after the July 2012 update of LaTeX3

\documentclass[12pt]{memoir}
\usepackage{xparse}
\ExplSyntaxOn

\tl_new:N \l_bruin_argi_tl
\tl_new:N \l_bruin_argii_tl
\tl_new:N \l_bruin_argx_tl
\tl_new:N \l_bruin_temp_tl
\tl_new:N \l_bruin_final_tl

% First of all we split the comma separated list; we store |#1|, the
% title of the book, in |\bruin_argi_tl| and |\bruin_argx_tl| to
% contain |\use_none:nn| that at the end will gobble the first two
% tokens (a comma and a space); then we put the split list of
% arguments followed by |{\NoValue}| after |\bruin_process:n|, because
% it will be used to terminate the recursion.
\NewDocumentCommand\bruin_dolist:nn {m >{\SplitList{,}}m}{
  \tl_set:Nn \l_bruin_argi_tl { #1 }
  \tl_set:Nn \l_bruin_argx_tl { \use_none:nn }
  \ProcessList{#2}{\bruin_process:n}
}
% |\bruin_process:n| does the recursion via \ProcessList; it
% stores its argument into |\bruin_argii_tl|. Then it appends also
% it to the list |\bruin_argx_tl|, after a comma and a space. The same
% contents is also processed by |\bruin_normalize:n|. Then we execute
% |\bruin_do:|.
\cs_new:Nn \bruin_process:n {
     \tl_set:Nn \l_bruin_argii_tl {#1}
     \tl_set:Nx \l_bruin_argx_tl { \l_bruin_argx_tl , \c_space_tl #1 }
     \bruin_normalize:n {#1} \bruin_do: 
}
% |\bruin_normalize:n| is responsible for splitting an entry of the
% form |A-B| (|A| and |B| will actually be something like |1.2|) into
% the two components, which are passed to |\bruin_normalizei:nn|
\NewDocumentCommand\bruin_normalize:n {>{\SplitArgument{1}{-}}m}{
  \bruin_normalizei:nn #1
}
% If the entry was |A-B|, |\bruin_normalizei:nn| is passed |{A}{B}|,
% otherwise it is passed |{A}{\NoValue}|
\cs_new:Nn \bruin_normalizei:nn {
  \IfNoValueTF{#2}
    {\bruin_three:n {#1}} % simple entry
    {\bruin_three:nn {#1}{#2}} % complex entry
}
% |\bruin_three:n| splits an argument of the form |1.2.3| into the
% components; it's quite similar to |\bruin_normalize:n|
\NewDocumentCommand\bruin_three:n {>{\SplitArgument{2}{.}}m}{
  \bruin_threedo:nnn #1
  \tl_set:Nx \l_bruin_final_tl {\l_bruin_temp_tl}
}
% |\bruin_threedo:nnn| sets |\l_bruin_final_tl| to the components but
% with three digits; |1| is transformed into |001| and |1.2| becomes
% |001.002|, while |1.2.3| becomes |001.002.003|
\cs_new:Nn \bruin_threedo:nnn {
  \IfNoValueTF{#3}
    {\IfNoValueTF{#2}
       {\tl_set:Nx \l_bruin_temp_tl
          {\bruin_threedig:n{#1}}}
       {\tl_set:Nx \l_bruin_temp_tl
          {\bruin_threedig:n{#1}.\bruin_threedig:n{#2}}}
    }
    {\tl_set:Nx \l_bruin_temp_tl
       {\bruin_threedig:n{#1}.\bruin_threedig:n{#2}.\bruin_threedig:n{#3}}
    }
}
% we do similarly for |\bruin_three:nn|, calling |\bruin_threedo:nnn| twice.
\NewDocumentCommand\bruin_three:nn
  {>{\SplitArgument{2}{.}}m >{\SplitArgument{2}{.}}m}
  { \bruin_threedo:nnn #1
    \tl_set:Nx \l_bruin_final_tl {\l_bruin_temp_tl }
    \bruin_threedo:nnn #2
    \tl_set:Nx \l_bruin_final_tl {\l_bruin_final_tl -- \l_bruin_temp_tl}
  }

\cs_new:Nn \bruin_do: {
  \cs_set:Nx \bruin_next: {
    \exp_not:N \index 
      {\l_bruin_argi_tl ! \l_bruin_final_tl \c_space_tl @ \l_bruin_argii_tl }
  }
  \bruin_next: 
}
\cs_new:Nn \bruin_threedig:n {
  \int_compare:nNnTF {#1} < {100}
    { 0 \int_compare:nNnTF {#1} < {10} {0}{} }
    {} #1
}

% |\bruin_defcite:nn| defines |#1| as a macro with one argument, first
% storing its second argument in it. So
%
% |\defcite{\citeT}{Title}|
%
% defines |\citeT| as if it were
%
% |\def\citeT#1{\emph{Title}\space\bruin_dolist:nn{Title}{#1}\l_bruin_argx_tl}|
%
% The token list |\l_bruin_argx_tl| will be built by |\bruin_dolist:nn|
\cs_new:Nn \bruin_defcite:nn {
  \cs_new:Npn #1##1{\emph{#2}\space\bruin_dolist:nn {#2}{##1} \l_bruin_argx_tl }}
\cs_set_eq:NN \defcite \bruin_defcite:nn
\ExplSyntaxOff

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

\makeindex

\begin{document}

%\tracingmacros=1
bla bla bla \citeherm{12.1-2}
\newpage
bla bla bla \citeherm{12.2,13.9}
\newpage
bla bla bla \citeherm{12.3-13.4}
\newpage
bla bla bla \citeherm{12}
\newpage
bla bla bla \citeherm{12.10}
\newpage
bla bla bla \citeherm{12.11}
\newpage
bla bla bla \citeherm{12.20}
\newpage
bla bla bla \citeherm{12.21}
\newpage


bla bla bla \citeash{1.2.3}
\newpage
bla bla bla \citeash{2.1}.
\newpage
bla bla bla \citeash{10.1,1.1,2.9-19}.
\newpage
\printindex

\end{document}
egreg
  • 1,121,712
  • Whoah, this is awesome. Just, ranges don't appear to work. For example "“bla bla bla” \citeherm{12.1-2}" gives an error "! Missing = inserted for \ifnum." – Tom de Bruin Oct 25 '11 at 11:43
  • @TomdeBruin You're right; I've edited the answer – egreg Oct 25 '11 at 12:32
  • Is there any way to expand this to accept the following \citeherm{12.1, 35.4, 28.2}? – Tom de Bruin Oct 25 '11 at 14:51
  • @TomdeBruin See edited answer – egreg Oct 25 '11 at 16:59
  • Firstly, thanks so much for the help! This is really amazing. Sorry to keep at this, but I found another issue. Sometimes you need to cite a range that spans chapters, e.g. \citeherm{1.2-3.4}... Is it possible to support his also? – Tom de Bruin Oct 27 '11 at 08:48
  • Once again, thanks for the help. As I kept adding and working, I noticed one more thing. Some documents don't have chapters :D For example, I need to cite "Plea for the Christians 26". Unfortunately \citeherm{26} gives an error... Is there any way to fix that? – Tom de Bruin Nov 02 '11 at 07:39
  • @TomdeBruin Is that the end? :) – egreg Nov 02 '11 at 14:13
  • @TomdeBruin I've added a new implementation that should be even more robust. – egreg Nov 15 '11 at 16:01
  • Ah, I went on holiday, I doubt it is the end :D Should I use the 'Latex 3' implementation? – Tom de Bruin Nov 23 '11 at 18:36
  • I just noticed a slight error in the latex 3 implementation. \cs_new:Npn #1##1{\emph{#2},\space\bruin_dolist:nn {#2}{##1} \bruin_argx: }} should read \cs_new:Npn #1##1{\emph{#2}\space\bruin_dolist:nn {#2}{##1} \bruin_argx: }}. (There is an extra ',' in there). – Tom de Bruin Nov 23 '11 at 18:53
  • @TomdeBruin It's very "experimental". I'll try to rework it. – egreg Nov 23 '11 at 18:59
  • I tried it on the my thesis, and didn't note any problems. So it seems to work fine. I'll keep noting issues if I run into them. – Tom de Bruin Nov 23 '11 at 19:47
  • (Returning to work) I have noticed a feature that would be nice if possible. Some of the works have a triple numbering scheme: 3.6.2 (book.chapter.verse). Would this be very difficult to implement? – Tom de Bruin Nov 23 '11 at 19:53
  • 1
    @TomdeBruin Maybe the last? :) This has also book.chapter.verse. You owe me a beer. – egreg Nov 23 '11 at 21:37
  • I need some more help now! Suddenly (with texLive 2012) this (Latex3 version) is no longer working... I don't know why, I get the following error: Undefined control sequence. \l_bruin_argx_tl ,\c_space_tl \NoValue – Tom de Bruin Aug 04 '12 at 08:51
  • @TomdeBruin There have been some changes in the LaTeX3 kernel. I'll try finding a workaround. In the meantime you should continue to use TeX Live 2011. – egreg Aug 04 '12 at 11:07
  • yeah, I will. I just kinda need the newest Biblatex... – Tom de Bruin Aug 04 '12 at 11:25
  • @TomdeBruin The new version should work also with TeX Live 2012. – egreg Aug 04 '12 at 15:44
  • Whoops.... Done now. – Tom de Bruin Aug 07 '12 at 11:59