27

I'm writing a textboox in latex, and I have things like

\textit{some definition}\index{Some Definition}

all over the place. I'd like to combine them into something like

\define{some definition}

and have it expand automagically. So I can have

\newcommand{\define}[2]{\textit{#1}\index{#2}}

which works, but forces me to include both arguments. The second argument is almost always the first argument with the first letter of each word uppercased. How can I write a command which uppercases the first character of each word?

Suggestions of better indexing best practices would also be appreciated.

18
\documentclass{article}
\usepackage{makeidx}\makeindex
\newcommand*{\formatfirst}[1]{\MakeUppercase{#1}}
\makeatletter
\newcommand*{\mymacro}[1]{%
  \expandafter\formatfirst\expandafter{\@car #1\@empty\@nil}%
  \@cdr #1\@empty\@nil}
\newcommand*\myMakeUpperCase[1]{%
  \def\@myuppercasewords{\myuppercase@i#1 \@nil}%
    {\itshape\@myuppercasewords}\index{#1@\@myuppercasewords}}
\def\myuppercase@i#1 #2\@nil{%
  \mymacro{#1}%
  \ifx\\#2\\%
  \else
    \@ReturnAfterFi{%
      \space
      \myuppercase@i#2\@nil
    }%
  \fi} 
\long\def\@ReturnAfterFi#1\fi{\fi#1} 
\makeatother

\begin{document}
foo
\myMakeUpperCase{capital letter} bar
\myMakeUpperCase{Next one} baz
\myMakeUpperCase{two words}

\printindex

\end{document} 

alt text

  • Close, but if I do it this way then the index isn't sorted, because all entries start with \MakeUpperCase. Is there any way to force that macro to expand before building the index? – Nate Dec 31 '10 at 0:12
  • 1
    @Nate: ah, I see. change the line to ` {\itshape\@myuppercasewords}\index{#1@\@myuppercasewords}}`. I edited also the code above and the output picture. – user2478 Dec 31 '10 at 6:50
  • Thanks! Out of curiosity, why does everybody leave percent signs where they open and close brackets? It just begins a comment, right? – Nate Dec 31 '10 at 15:53
  • 3
    Also, just realized that if you have two entries for "alpha" and "Alpha" then the index will display them both as "Alpha" but won't combine them into the same entry like it should. Again, I don't suppose there's any way to make the uppercase macro expand earlier? I can just write a pre-processing script that uppercases the character in the ind file, but it would be nice to have it done in tex. – Nate Dec 31 '10 at 16:02
  • 3
    @Nate: The percent signs begin a comment, which swallows white space. If you didn't use them, then, for some mis-defined command \foo, if you wrote \foo., it would typeset incorrectly as foo . – Antal Spector-Zabusky Dec 31 '10 at 19:23
12

The mfirstuc package from the glossaries bundle provides the handy \capitalisewords command which could be used here. I adapted Herbert's example and the output is the same:

\documentclass{article}

\usepackage{mfirstuc}
\usepackage{makeidx}\makeindex

\makeatletter
\newcommand*\define[1]{%
  \textit{#1}%
  \index{#1@\protect\capitalisewords{#1}}%
   }
\makeatother

\begin{document}
foo
\define{capital letter} bar
\define{Next one} baz
\define{two words}

\printindex

\end{document}
6

I am not sure if I understand your question correctly, but a short macro as shown below, will capitalize the first word you type and place it in the index.

\documentclass[11pt]{article} 
\usepackage{index}
\makeindex
\begin{document}
\def\Index#1{\def\x##1##2{\MakeUppercase{##1}{##2}}\textit{\x#1} \index{\x#1}} 
\Index{alpha}

\def\indeX#1#2{\def\x##1##2{\MakeUppercase{##1}{##2}}\x#1 \index{\x#2}} 
\Index{alpha}

\indeX{test}{this}

\printindex
\end{document}
  • Yes, but how would I make this work with \Index{alpha beta}? – Nate Dec 30 '10 at 18:15
  • @Nate what is it exactly you want to print and what do you want to place in the Index? – Yiannis Lazarides Dec 30 '10 at 18:52
  • @Nate see edit. – Yiannis Lazarides Dec 30 '10 at 19:07
  • I modified this to keep the index sorted: \def\index#1{\def\x##1##2{\MakeUppercase{##1}{##2}}#1\inindex{#1@\x#1}} – fuenfundachtzig Nov 21 '11 at 9:01
4

My pure LaTex3 solution:

\documentclass{minimal}
\usepackage{xparse}
\usepackage[T1]{fontenc}

\ExplSyntaxOn

\NewDocumentCommand{\ucFirst}{m} {
  \sphakka_ucfirst:f {#1}
}
\cs_new_protected:Npn \sphakka_ucfirst:f #1 {
  \exp_last_unbraced:Nx \tl_to_uppercase:n {\tl_head:f {#1}}
  % notice the ~ to put back blank padding
  \tl_tail:f {#1}~
}

\NewDocumentCommand{\ucFirstMore}{m} {
  \sphakka_ucfirstmore:f {#1}
}
\cs_new_protected:Npn \sphakka_ucfirstmore:n #1 {
  % must convert the TL into a SEQ
  \seq_set_split:Nnn \l_sphakka_seq {~} {#1}
  \seq_map_function:NN \l_sphakka_seq \sphakka_ucfirst:f
}
\cs_generate_variant:Nn \sphakka_ucfirstmore:n {f}

\ExplSyntaxOff

\begin{document}

\def\word{bar}
\def\sentence{foo bar baz}

[Plain word] quux => \ucFirst{quux}\par
[Word via macro]~\word~=> \ucFirst{\word}\par
[Plain sentence] foo bar baz => \ucFirstMore{foo bar baz}\par
[Sentence via macro]~\sentence~=> \ucFirstMore \sentence \par

\end{document}

Though simple, the above solution leaves a trailing non-breakable space which is rather tricky to get rid of. Thus, I wrote a variant that stores capitalized tokens into another seq, then glues them by spaces:

\documentclass{minimal}
\usepackage{xparse}
\usepackage[T1]{fontenc}

\ExplSyntaxOn

\NewDocumentCommand \ucFirst {>{\TrimSpaces}m} {
  % Won't trim explicit trailing `\space's... why?
  \sphakka_ucfirst:f {#1}
}
\cs_new_protected:Npn \sphakka_ucfirst:f #1 {
  \exp_last_unbraced:Nf \tl_to_uppercase:n {\tl_head:f {#1}}
  \tl_tail:f {#1}
}

\NewDocumentCommand \ucFirstMore {>{\TrimSpaces}m} {
  \sphakka_ucfirstmore:f {#1}
}
\seq_new:N \l_out_seq
\cs_new_protected:Npn \sphakka_ucfirstmore:n #1 {
  % split by blanks. Can anybody tell me why `\space` doesn't work?
  \seq_set_split:Nnn \l_in_seq {~} {#1}
  % remove empty/blank items
  \seq_remove_all:Nn \l_in_seq {}
  \seq_remove_all:Nn \l_in_seq {\space}
  % capitalize each token and store it into another seq
  \seq_map_inline:Nn \l_in_seq {
    \seq_put_right:Nn \l_out_seq {\sphakka_ucfirst:f {##1}}
  }
  \seq_use:Nnnn \l_out_seq {\space}{\space}{\space}
  \seq_clear:N \l_out_seq
}
\cs_generate_variant:Nn \sphakka_ucfirstmore:n {f}


\ExplSyntaxOff

\begin{document}

\edef\word{\space  quux\space }
\edef\sentence{  \space  foo  bar baz \space }

\begin{tabular}[c]{p{.6\linewidth}l}
  \verb|\word|                                          & \verb|`\space  quux\space '|\\
  \verb|\sentence|                                      & \verb|`\space  foo  bar baz \space '|\\
  \\
  \verb|\ucFirst{\space  quux\space }|                  & `\ucFirst{\space  quux\space }'\\
  \verb|\ucFirst{\word}|                                & `\ucFirst{\word}' \\
  \verb|\ucFirstMore{\word}|                            & `\ucFirstMore{\word}' \\
  \verb|\ucFirstMore{  \space  foo  bar baz \space }|   & `\ucFirstMore{  \space  foo  bar baz \space }'\\
  \verb|\ucFirstMore{\sentence}|                        & `\ucFirstMore{\sentence}'\\
\end{tabular}

\end{document}

lualatex output

There are just a couple of minor glitches that I couldn't resolve (possibly because of my inexperience...):

  • Why \TrimSpaces (or \tl_trim_spaces) doesn't remove explicit trailing \spaces?

  • Why \seq_set_split cant't split by \space?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.