Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

To enforce labeling regulations in a document to be edited by ~50 users (imported subdocuments) i have to re-redine the existing document structuring commands part, chapter, etc to follow a predefined labeling scheme (prefixing with hierarchy level to avoid collisions).

As examples speak more than words, here my current definition for chapter (omitting details). Basically, both \chapter and \chapter* are extended with an optional parameter used to define a label which is automatically prefixed by "chap:", reusing the old \chapter command. Additionally, a command for creating references is defined where short vs. long reference text is chosen via the boolean variable \iflongrefs.

\RequirePackage{letltxmacro}
\RequirePackage{xargs}

\newcommand*\section@namelong{Section}%
\newcommand*\section@nameshort{Sect.}%
\newcommand*\section@prefix{sect}%

\LetLtxMacro{\section@old}{\section}%
\renewcommand\section{\@ifstar \section@star \section@nostar}%
\newcommandx*\section@nostar[3][1=,3=]{%
    \ifx &#1&%
        \section@old{#2}%
    \else%
        \section@old[#1]{#2}%
    \fi%
    \ifx &#3&%
    \else%
        \label{\section@prefix:#3}%
    \fi%
}%
\newcommandx*\section@star[2][2=]{%
    \section@old*{#1}%
    \ifx &#2&%
    \else%
        \label{\section@prefix:#2}%
    \fi%
}%

\newcommand*\sectionref[1]{
    \iflongrefs
        \section@namelong~\ref{\section@prefix:#1}
    \else
        \section@nameshort\,\ref{\section@prefix:#1}
    \fi
}

Now, I need similar definitions for part, subsection, etc... Of course I can copy & paste this, replacing \section@XXX with \part@XXX etc accordingly. However I'd prefer a more elegant solution, such as a "higher-order command" \extend encapsulating the (re)definitions. A call to \extend for chapters should look like the line given below, and produce all the code seen above.

\extend{section}{Section}{Sect.}{sect}

How can I make this work? Reading through various forum posts I feel \expandafter is the way to go, but I did not understand the examples enough to get it working for me. Any help?

share|improve this question

1 Answer 1

up vote 2 down vote accepted

With the help of etoolbox (but only for convenience): just change all \section into #1, but using \csdef and \csuse; the important thing is to refer to arguments of macros defined inside the metamacro by doubling # symbols.

\documentclass{article}
\makeatletter
\RequirePackage{etoolbox}
\RequirePackage{xargs}

\newif\iflongrefs

\newcommand{\extend}[4]{%
  \csdef{#1@namelong}{#2}%
  \csdef{#1@nameshort}{#3}%
  \csdef{#1@prefix}{#4}%
  \csletcs{#1@old}{#1}%
  \csdef{#1}{\@ifstar{\csuse{#1@star}}{\csuse{#1@nostar}}}%
  \expandafter\newcommandx\expandafter*\csname#1@nostar\endcsname[3][1=,3=]{%
    \ifx &##1&%
        \csuse{#1@old}{##2}%
    \else
        \csuse{#1@old}[##1]{##2}%
    \fi
    \ifx &##3&%
    \else
      \label{\csuse{#1@prefix}:##3}%
    \fi
  }%
  \expandafter\newcommandx\expandafter*\csname#1@star\endcsname[2][2=]{%
    \csuse{#1@old}*{##1}%
    \ifx &##2&%
    \else
      \label{\csuse{#1@prefix}:##2}%
    \fi
  }
  \csdef{#1ref}##1{%
    \iflongrefs
        \csuse{#1@namelong}~\ref{\csuse{#1@prefix}:##1}%
    \else
        \csuse{#1@nameshort}\,\ref{\csuse{#1@prefix}:##1}%
    \fi
  }%
}
\makeatother

\extend{section}{Section}{Sect.}{sect}
\extend{subsection}{Subection}{Subsect.}{subsect}

\begin{document}

\section{abc}

\section[aaa]{abc}[def]


\sectionref{def}

{\longrefstrue\sectionref{def}}

\subsection[uuu]{vvv}[www]

\subsectionref{www}

\end{document}

Note that \LetLtxMacro is not necessary, as \section and friends are not defined to have optional arguments.

Added – xparse version

Instead of xargs you may want to use the modern xparse package:

\RequirePackage{etoolbox}
\RequirePackage[log-declarations = false]{xparse}

\newcommand{\extend}[4]{%
  \csdef{#1@namelong}{#2}%
  \csdef{#1@nameshort}{#3}%
  \csdef{#1@prefix}{#4}%
  \csletcs{#1@old}{#1}%
  \expandafter\DeclareDocumentCommand\csname#1\endcsname{s o m o}{
    \IfBooleanTF{##1}
      {\csuse{#1@old}*{##3}
%       \IfNoValueTF{##4}
%         {}
%         {\label{\csuse{#1@prefix}:##4}}
      }
      {\IfNoValueTF{##2}
         {\csuse{#1@old}{##3}}
         {\csuse{#1@old}[##2]{##3}}
       \IfNoValueTF{##4}
         {}
         {\label{\csuse{#1@prefix}:##4}}
      }
  }
  \expandafter\DeclareDocumentCommand\csname#1ref\endcsname{m}{
    \iflongrefs
        \csuse{#1@namelong}~\ref{\csuse{#1@prefix}:##1}
    \else
        \csuse{#1@nameshort}\,\ref{\csuse{#1@prefix}:##1}
    \fi
  }%
}

The three commented lines are because it's not clear what's the purpose of a label to a \section* command.

share|improve this answer
    
Really works like a charm - thank you so much! Btw I kept the lines you commented out as I extended the code to also build \XXXpageref commands which can definitely make sense for unnumbered elements as well. –  DrTH Nov 1 '11 at 5:18

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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