31

I have a command like

\newcommand{\todo}[1]{#1}

which I sometimes replace by:

\newcommand{\todo}[1]{}

In terms of spacing/whitespace, the behavior of the "empty" command is as follows:

Test.

\todo{foo} % does not generate (vertical) whitespace => good

Test\todo{foo}. Test. % does not generate (horizontal) whitespace => good

Test\todo{foo}. \todo{foo} Test. % _does_ generate (horizontal) whitespace => bad

Is there any way to avoid this, i.e., to (temporarily) replace a command in a way so that it is fully equivalent to removing all occurrences of the command?

(I know that there are packages like todonotes or verbatim, which provide similar functionality. But I always wanted to know how to solve this manually, and never had an idea how to approach this / what to search for. Using \xspace or \empty in the command did not work at least.)

32

The classical way is using \@bsphack and \@esphack. They are used by \label or \index, both supposed to be invisible/empty regarding spacing. These commands remember, if there is a space before the command. If yes, the second space afterwards is suppressed by \ignorespaces. The commands are defined in the LaTeX kernel:

\def\@bsphack{%
  \relax
  \ifhmode
    \@savsk\lastskip
    \@savsf\spacefactor
  \fi}
\def\@esphack{%
  \relax
  \ifhmode
    \spacefactor\@savsf
    \ifdim\@savsk>\z@
      \ignorespaces
    \fi
  \fi}

Applied to \todo:

\documentclass{article}

\makeatletter
\newcommand{\todo}[1]{%
  \@bsphack
  \@esphack
}
\makeatother

\begin{document}
Test.

\todo{foo}

Test\todo{foo}. Test.

Test\todo{foo}. \todo{foo} Test.
\end{document}

Result

Limitation: The method can fail, if the macros using \@bsphack and \@esphack are used one after each other. The second \@bsphack does not know the state of the previous one. Therefore \ignorespaces can be suppressed leaving the following space.

| improve this answer | |
  • Works like a charm, thanks! Apparently, the occurrence of a command will internally never be equivalent to the case of a "non-occurrence". So I guess the best solution here is to emulate the non-occurrence. – bluenote10 Sep 18 '14 at 12:39
15

It doesn't really generate space it's just that you have added two spaces, you would see same from a {} b with one space before and one after. But you can end your command with \ignorespaces to ignore the following space.

\newcommand{\todo}[1]{\ignorespaces}
| improve this answer | |
  • This is looking good, but: In this case Test\todo{foo}.\todo{foo} Test. behaves "unexpected", since it is not the same as Test\todo{foo}. Test. -- any idea to solve this as well? – bluenote10 Sep 18 '14 at 12:15
  • @bluenote10 \ignorespaces is simpler, the more complicated thing is to see if there was a space before and just ignorespaces in that case) that's what the latex \@bspack macro does (it is used in \label{} and \index{] and similar commands that are supposed to be "invisible"). I thought I'd just do the simple one, but if you want the other, give the tick to Heiko:-) – David Carlisle Sep 18 '14 at 12:32
  • Thanks for the clarification! Considering the educational aspect of my question, your answer is highly appreciated :). – bluenote10 Sep 18 '14 at 12:35
6

You put nothing into the horizontal/vertical list when \toto is empty. Thus the \spacefactor manipulating is redundant (see the accepted answer). The following code is sufficient for this task:

\def\hideme{\ifdim\lastskip>0pt \ignorespaces\fi}
\def\todo#1{#1}
% or:
\def\todo#1{\hideme}
| improve this answer | |

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.