2

My goal is to redefine the command \section with a new argument for labeling: \section[Title in TOC]{Title}{label}

\documentclass{article}

\let\oldsection\section
\renewcommand{\section}[3][]{\oldsection[#1]{#2}\label{sec:#3}}

\begin{document}
\tableofcontents

\section{Myhello}{myhello}

\section[Hello in TOC]{Hello}{hello}
See section \ref{sec:myhello}.

\end{document}

I want to do it like in https://stackoverflow.com/a/1812477/2062965. Is \renewcommand better style than using \def?

3
  • 2
    Adding the label as final argument is not a good idea: you just spare the \label command and lose in clarity. The syntax of \section already accepts an optional argument for the TOC entry. – egreg Apr 19 '13 at 8:36
  • Ok \section might not be a good example to generalize but there are others... Nevertheless I prefer getting reminded if I forgot to label a section. Sounds this reasonable? – strpeter Apr 19 '13 at 8:57
  • A label is useful only when it's used in a \ref. And you won't get reminded if you forget the argument: you'll only get puzzling output. Using a template in your editor is probably better. – egreg Apr 19 '13 at 9:03
8

It is not possible to implement you syntax using high-level LaTeX commands such as \renewcommand to provide the optional argument syntax that you are looking for.

For user commands LaTeX2e offers only a very simple method to define optional arguments: you can have the first argument optional and you can define a default value for it. What you can't do with this mechanism is what is needed in the case of a heading: to use the same value for both title and TOC if only title is given. If you look at your implementation then you see that you pass an empty argument to the optional argument of \oldsection resulting in an empty TOC title later on.

The heading commands inside the LaTeX kernel do some more elaborate work to use the title argument twice if no optional argument for the TOC was given. That you would need to provide as well if you want to extend/change the interface. But as @egreg commented on it may be a questionable gain in the first place.

One way to nicely get this working would be to use the extended possibilities of xparse. I changed you input a bit to make the label argument optional (just to show off what xparse do) so now the implemented syntax is:

\section * [TOC] {title} [label]

The necessary code for this is shown below, it is a bit backward as depending on the optional args given and the star given or not given different versions of \oldsectionare called, as that command doesn't know about the xparse conventions.

\documentclass{article}

\usepackage{xparse}

\let\oldsection\section  % save the old command

\RenewDocumentCommand\section {s o m o}
   {
     \IfNoValueTF{#2}
          {                                                      % no TOC arg
             \IfBooleanTF{#1}                       
                 { \oldsection*{#3} }                                % star given
                 { \oldsection {#3} }                                % no star given
          }
           {                                                     % optional TOC there
              \IfBooleanTF{#1}
                 { \oldsection*[#2]{#3} }
                 { \oldsection[#2]{#3}  }
         }   
    \IfNoValueF{#4}                % optional label given, if not we do nothing
        {
            \label{sec:#4}
         }
  }

\begin{document}
\tableofcontents

\section{Myhello}[myhello]

\section[Hello in TOC]{Hello}[hello]
See section \ref{sec:myhello}.

\end{document}

Update

Just realized that I allowed for both * and optional TOC argument and passed both to \oldsection. That doesn't actually work as in 2e the heading doesn't support the optional argument when the star is used.

So here is a slightly different definition: if both star and optional argument are used then we generate a TOC entry using the optional argument manually. Alternatively one raise an error or simply ignore it.

\RenewDocumentCommand\section {s o m o}
   {
    \IfBooleanTF{#1}                       
        { \oldsection*{#3} 
          \IfNoValueF{#2}            % if TOC arg is given create a TOC entry
            { \addcontentsline{toc}{section}{#2}  }
        }  
      {                              % no star given 
        \IfNoValueTF{#2}
          { \oldsection {#3} }       % no TOC arg
          { \oldsection[#2]{#3}  }
      }   
    \IfNoValueF{#4}                  % optional label given, if not we do nothing
        { \label{sec:#4} }
  }
5

\section has three syntax forms:

  • \section{TXT}
  • \section[TOC]{TXT}
  • \section*{TXT}

\label does not make much sense for the third form, because starred \sections are unnumbered.

\documentclass{article}

\makeatletter
\newcommand*{\old@section}{}
\let\old@section\section
\renewcommand*{\section}{%
  \@ifstar\old@section\new@section
}
\newcommand*{\new@section}{%
  \@ifnextchar[\new@@section\new@@@section % ]
}
\def\new@@section[#1]#2#3{%
  \old@section[{#1}]{#2}\label{sec:#3}%
}
\newcommand*{\new@@@section}[2]{%
  \old@section{#1}\label{sec:#2}%
}
\makeatother

\begin{document}
\tableofcontents % uses \section*{...}

\section{Myhello}{myhello}

\section[Hello in TOC]{Hello}{hello}
See section \ref{sec:myhello}.

\end{document}

Remarks:

  • \@ifstar has two arguments. It looks, whether the next token after the arguments and optional spaces is a star *. If yes, the next argument is used and the star is automatically removed. Otherwise the second argument is called.

  • \@ifnextchar[ takes three arguments. If the token after the arguments and optional spaces is the same as the first argument, then the second argument is called, otherwise the third. The "next char" is not automatically removed.

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.