9

I've defined a command which lets me quickly reference to the number (of figure or table) AND page at the same time:

Command

 \newcommand{\kplref}[2]{~\ref{#1}, S.~\pageref{#1}{#2}\xspace}

The second argument is used for adding something like "page 6f" or "page 6ff", but I would like to have it optional and do not want to be forced to enter an empty {} each time.

However, defined like that the command takes the first letter of a following word as its second argument (see example).

  • How can I change the command definition to avoid that?
  • Or is there maybe already a ready-made command which does exactly what I want?

Result

enter image description here

Example document

\documentclass[11pt, parskip=half]{scrbook}


\usepackage{xspace}
\newcommand{\kplref}[2]{~\ref{#1}, S.~\pageref{#1}{#2}\xspace}

\begin{document}

%\tableofcontents

\section{Section1}
\label{sec1}

This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph.

This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph.

\section{Section2}

Now lets refer to section~\kplref{sec1}.

It fails if other text is following and there is no second argument like here:\newline 
\kplref{sec1} shows the nonsense of this document.

\end{document}
  • 2
    In LaTeX, if a macro has two arguments and one of them is supposed to be optional, it is the first argument rather than the second one that's the optional one. – Mico Mar 15 '15 at 7:27
8

You should define your command as

\newcommand{\kplref}[2][]{\ref{#2}, S.~\pageref{#2}#1\xspace}

and use the optional argument as in

\kplref[f]{sec1}

MWE:

\documentclass[11pt, parskip=half]{scrbook}


\usepackage{xspace}
\newcommand{\kplref}[2][]{\ref{#2}, S.~\pageref{#2}#1\xspace}

\begin{document}

%\tableofcontents

\section{Section1}
\label{sec1}

This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph.

This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph.

\section{Section2}

Now lets refer to section~\kplref{sec1}.

It doesn't fail if other text is following and there is no optional argument like here:\newline
\kplref{sec1} shows the nonsense of this document.

This is how to use the optional argument: \kplref[f]{sec1} which shows again the nonsense of this document.

\end{document} 

enter image description here

4

This isn't a direct answer to your question about how to set up macros with optional arguments. Instead, it's a suggestion for a hopefully even better solution to the problem you're looking to solve.

If I understand your setup correctly, you would like to have a method for referencing some items -- sections in your example, but other types of items should also be permissible -- by number only if the call-out is on the same page as the item itself, and by number plus some form of page numbering if the item is somewhere else. If this interpretation is correct, you should look into the varioref package and its \vref macro, which handles these tasks automatically.

Running the example code below, the first page looks like this

enter image description here

while the second page looks like this -- note the additional string "on the previous page", courtesy of the \vref macro.

enter image description here

\documentclass[11pt, parskip=half]{scrbook}
\usepackage{varioref} % for \vref macro
\begin{document}
\setcounter{chapter}{1} % just for this example

\section{Thoughts} \label{sec1}
This is a nonsense paragraph. 

\section{More thoughts}
Now let's refer to Section~\vref{sec1}.

\newpage % force a page break
Now let's again refer to Section~\vref{sec1}.
\end{document}

Addendum: If you additionally load the cleveref package (after varioref), you could write Now let's again refer to~\vref{sec1}. (note: no "Section" string) and you'd get

Now let's again refer to Section 1.1 on page 1.

i.e., you'd get a numeric page reference ("on page 1") instead of the string "on the preceding page".

  • thanks. I know about the varioref package, but its results do add words/expressions which significantly change the length of a sentence/paragraph. As I try to optimize (shorten) my document, this would lead (I fear) to permanent changes, so I'd prefer a very short solution instead of the sophisticated "full text" variant, which varioref creates. – MostlyHarmless Mar 15 '15 at 8:24
  • 1
    @Martin - If you combine varioref with cleveref, you'll get shortened page-related references -- e.g., "on page 1" rather than "on the previous page". The varioref manual discusses various ways for customizing its output. – Mico Mar 15 '15 at 8:30
4

You can use the xargs package

\documentclass[11pt, parskip=half]{scrbook}


\usepackage{xspace,xargs}

\newcommandx{\kplref}[2][2]{\ref{#1}, S.~\pageref{#1}#2\xspace}% removed ~ from OP


\begin{document}

\section{Section1}
\label{sec1}

This is a nonsense paragraph. This is a nonsense paragraph. This is a
nonsense paragraph. This is a nonsense paragraph. This is a nonsense
paragraph. This is a nonsense paragraph.

This is a nonsense paragraph. This is a nonsense paragraph. This is a
nonsense paragraph. This is a nonsense paragraph. This is a nonsense
paragraph. This is a nonsense paragraph.

\section{Section2}

Now lets refer to section~\kplref{sec1}, or even to
section~\kplref{sec1}[ff] to check also that the following space is not gobbled.

It does not fail if other text is following and there is no second
argument like here:\newline \kplref{sec1} shows the true sense of this
document.

\end{document}
4

I'm not sure what you're after, it seems to me that you need only one optional argument. If you write just \kplref{bla} shows, then even without any usage of \xspace, you will get the space, and you won't get it in as was shown by \kplref{bla}.

But anyways, this is an xparse solution, highly unrecommended, because the last parameter should not be optional.

\documentclass[11pt, parskip=half]{scrbook}

\usepackage{xparse}

\usepackage{xspace}
\DeclareDocumentCommand \kplref {mO{}} {~\ref{#1}, S.~\pageref{#1}#2\xspace}
%\newcommand{\kplref}[2]{~\ref{#1}, S.~\pageref{#1}{#2}\xspace}

\begin{document}

%\tableofcontents

\section{Section1}
\label{sec1}

This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph.

This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph. This is a nonsense paragraph.

\section{Section2}

Now lets refer to section~\kplref{sec1}.

It fails if other text is following and there is no second argument like here:\newline 
\kplref{sec1}[]shows the nonsense of this document.

\end{document}
  • Why the last argument should not be optional? – Manuel Mar 15 '15 at 15:42
  • because then [ following the command is ambiguous: you dont' know whether it's an optional argument or a piece of text. As well, commands whose last argument is optional cannot be expandable (at least not in the xparse model, not sure about other situations). – yo' Mar 15 '15 at 15:46
  • Okey, still, the general assumption is not necessarily good, I like \setmainfont{Font}[key=value]. I don't like \usepackage[<a thousand of key=values with line breaks>]{package} – Manuel Mar 15 '15 at 15:55
  • @Manuel Ah that's a setup command, these are completely different in nature! For one, they are never mixed up with text, and for second, they never appear in expandable context. – yo' Mar 15 '15 at 15:57
  • That's what I meant with “general assumption” :) since you didn't write the reasons, I was looking in case you just preferred all optionals to go before. – Manuel Mar 15 '15 at 16:00
4

Here are some possibilities:

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand{\kplref}{m}{%
  \unskip~\ref{#1}, S.~\pageref{#1}%
}

\NewDocumentCommand{\kplrefA}{mo}{%
  \unskip~\ref{#1}, S.~\pageref{#1}
  \IfValueTF{#2}{#2}% <--- this adds the possibility of manipulating the argument
}

\NewDocumentCommand{\kplrefB}{mG{}}{%
  \unskip~\ref{#1}, S.~\pageref{#1}%
  \IfValueTF{#2}{#2}% <--- this adds the possibility of manipulating the argument
}

\begin{document}

\section{Title}\label{aaa}

Here are usage examples:
\begin{itemize}
\item Example of \verb|\kplref| for \kplref{aaa} and something
\item Example of \verb|\kplref| for \kplref{aaa}ff and something
\item Example of \verb|\kplrefA| for \kplrefA{aaa} and something
\item Example of \verb|\kplrefA| for \kplrefA{aaa}[ff] and something
\item Example of \verb|\kplrefB| for \kplrefB{aaa} and something
\item Example of \verb|\kplrefB| for \kplrefB{aaa}{ff} and something
\end{itemize}

\end{document}

Note that it's very simple just adding ff after the one parameter macro. The \unskip trick allows you to forget writing \kplref next to the preceding word.

enter image description here

My preference would be, however, for a different approach, where the optional argument comes first and no tie is implicitly used:

\NewDocumentCommand{\kplref}{om}{%
  \ref{#2}, S.~\pageref{#2}%
  \IfValueT{#1}{#1}% <--- this adds the possibility of manipulating the argument
}

to be used like

\item Example of \verb|\kplref| for~\kplref{aaa} and something

\item Example of \verb|\kplref| for~\kplref[ff]{aaa} and something

What do I mean with “possibility of manipulating the argument”? Suppose your abbreviations ff need to become ff. because of some picky copy editor's request. Using the last approach, but it's the same with \kplrefA and \kplrefB you just have to change the definition into

\NewDocumentCommand{\kplref}{om}{%
  \ref{#2}, S.~\pageref{#2}%
  \IfValueT{#1}{#1.\@}%
}

and upon reprocessing of the document, the periods will appear.

4

You can do it this way.

\documentclass[11pt, parskip=half]{scrbook}


\usepackage{xspace}

\newcommand{\kplref}[1]{\ref{#1}, S.~\pageref{#1}\kplpref}% removed ~ from OP
\newcommand{\kplpref}[1][]{#1\xspace }
% (my initially posted code did a completely silly check for emptiness of #1, 
% which means no optional argument was passed, or [], but after all what's
% wrong with an empty #1?)


\begin{document}

\section{Section1}
\label{sec1}

This is a nonsense paragraph. This is a nonsense paragraph. This is a
nonsense paragraph. This is a nonsense paragraph. This is a nonsense
paragraph. This is a nonsense paragraph.

This is a nonsense paragraph. This is a nonsense paragraph. This is a
nonsense paragraph. This is a nonsense paragraph. This is a nonsense
paragraph. This is a nonsense paragraph.

\section{Section2}

Now lets refer to section~\kplref{sec1}, or even to
section~\kplref{sec1}[ff] to check also that the following space is not gobbled.

It does not fail if other text is following and there is no second
argument like here:\newline \kplref{sec1} shows the true sense of this
document.

\end{document}

enter image description here

  • The \xspace is there to handle the case of a non existent optional argument. Indeed it is a known feature of LaTeX that when a command defined by its means looks ahead to see if there is a [ for an optional argument (always supposed to come first), it gobbles spaces. In the present case, if one is found, then the input will generally have [optional]<space>, hence the \xspace does not add anything, but if no optional argument is found, then the \xspace is handy indeed to put one back (if appropriate). – user4686 Mar 15 '15 at 7:50
1

If I understand your demand, you need to declare the macro with usage \kplref{first} or \kplref{first}{second}. I.e., if the open brace immediately follows the first argument then the second optional argument is scanned here, else the second argument is empty.

From TeX point of view this can be declared by:

\def\kplref#1{\def\tmp{#1}\futurelet\next\kplrefA}
\def\kplrefA{\ifx\next\bgroup \expandafter\kplrefB \else
   \expandafter\kplrefB\expandafter{\expandafter}\fi}
\def\kplrefB#1{mandatory argument: \tmp, optional argument: #1.}

\kplref{test},  \kplref{first}{second}.

\bye

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.