# I want to create a command that will conditionally gobble up trailing whitespace: I want an “\unxspace” command

I have a command that occasionally produces whitespace that I don't want.

\def\testa{a}
\def\testb{a}
\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\fi}


When \testa and \testb are different, I wind up getting unwanted whitespace in the following example:

    Testing \mycommand{This} \mycommand{is} \mycommand{my} \mycommand{trial} \mycommand{run.} Testing


I thought I could do something clever like

\def\sweetnothing{}
\def\testa{a}
\def\testb{b}
\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\else\sweetnothing\fi}


I thought this would make the test line above equivalent to the following

Testing \sweetnothing \sweetnothing \sweetnothing \sweetnothing Testing


But it's not.

I tried some \expandafter magic

\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\else\expandafter\sweetnothing\fi}


Since I thought that would essentially unwind to \sweetnothing followed by a blank space. No such luck.

Here's a M(non)WE:

\documentclass{article}
\makeatletter
%%
\def\testa{a}
\def\testb{b}
\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\fi}
%%
\def\sweetnothing{}
\newcommand{\mycommandvar}[1]{\ifx\testa\testb{#1}\else\sweetnothing\fi}
%%
\makeatother
\pagestyle{empty}
\begin{document}
\textbf{Line 1:} Testing \mycommand{This} \mycommand{is} \mycommand{my} \mycommand{trial} \mycommand{run.} Testing

\textbf{Line 2:} Testing \mycommandvar{This} \mycommandvar{is} \mycommandvar{my} \mycommandvar{trial} \mycommandvar{run.} Testing

\textbf{Line 3:} Testing {} {} {} {} {} Testing

\textbf{Line 4:} Testing \sweetnothing \sweetnothing \sweetnothing \sweetnothing Testing
\end{document}


I really wanted all three lines to look like line 4 when \testa and \testb are different.

Then I went off the deep end and tried something along the lines of

\documentclass{article}
\makeatletter
\def\testa{a}
\def\testb{b}
\def\eat@white@space#1{\ifcat #1\relax\else#1\fi}
\newcommand{\mycommand}[2]{\ifx\testa\testb{#1#2}\else\expandafter\eat@white@space\fi}
\makeatother
\pagestyle{empty}
\begin{document}
Testing \mycommand{This} \mycommand{is} \mycommand{my} \mycommand{trial} \mycommand{run.} Testing
\end{document}


But this had completely unexpected results. I didn't really expect it to work, but I didn't expect to quite fail like this either.

What I thought I was doing was comparing the catcode of a space to the catcode passed of the first argument. If they were the same, then I wanted to discard the second argument (at that point there should be a recursive call---which I don't do). If they are not the same, then I want to keep the first argument.

Any ideas?

-
@PeterGrill That works!!! –  A.Ellett Feb 22 '13 at 2:40
@PeterGrill How is that working? Based upon what I've read \unskip only applies to trailing glue on the line. Since I've got more text to come, what is it doing? –  A.Ellett Feb 22 '13 at 2:44

Let's see what happens: if \testa and \testb are equal, there is no problem; when \testa and \testb are different, from

Test \mycommand{this} is


you get "Test••is" (with • I denote a space in the output). This has nothing to do with the macro at hand, but it's a consequence of the rules. In fact, when \mycommand{this} is expanded to nothing, the input has already been tokenized, so TeX is not any more in the state where consecutive spaces are reduced to one space token.

How to remove the space depends on what you want to do. The difference between

\newcommand{\mycommand}[1]{%
\ifx\testa\testb
#1%
\else
\ifhmode\unskip\fi
\fi}


and

\newcommand{\mycommand}[1]{%
\ifx\testa\testb
#1%
\else
\ignorespaces
\fi}


is about what of the two spaces is removed; with \unskip it's the space before, with \ignorespaces it's the one after.

There is, however, a conceptual difference: \unskip just removes glue if it was there, and has nothing to do with macro expansion; \ignorespaces, instead, expands the following tokens until finding a non expandable one that is not a space token and gobbling all space tokens it finds in the process. (That's why it's not necessary to use \expandafter in front of it in the second definition, because it will start expanding the \fi).

Thus it may be preferable to use the first mode, if you're afraid that expansion can happen too early. Using \relax before \ifx, if you don't need \mycommand in an expansion only context, may prove useful.

Just to try being clearer, \unskip works down in the stomach, when commands are being executed; to the contrary, \ignorespaces works during macro expansion, much earlier than the stage when TeX executes commands. So the former can remove the last node, if it is glue, the latter cannot remove spacing instructions, but only space tokens.

-
I'm not sure I understand the "expands the following tokens until finding a non exapndable one that is not a space token". For example, \hspace*{1em} or \  or ~ don't seem to get gobbled up by ignorespaces. What am I not understanding? –  A.Ellett Feb 22 '13 at 14:48
@A.Ellett \ignorespaces gobbles space tokens not "spacing instructions". –  egreg Feb 22 '13 at 14:53

@egreg has already answered the main question so I thought I would explain

\documentclass{article}
\makeatletter
\def\testa{a}
\def\testb{b}
\def\eat@white@space#1{\ifcat #1\relax\else#1\fi}
\newcommand{\mycommand}[2]{\ifx\testa\testb{#1#2}\else\expandafter\eat@white@space\fi}
\makeatother
\pagestyle{empty}
\begin{document}
Testing \mycommand{This} \mycommand{is} \mycommand{my} \mycommand{trial} \mycommand{run.} Testing
\end{document}


\ifcat expands tokens until it finds two non expandable tokens and then compares their category codes (any non-character tokens assumed a fake catcode of 16 for this purpose). the two non expandable tokens are consumed in the test. If the test is true processing starts from the next token, and \else is defined to skip to the matching \fi. If the test is false TeX skips to the matching \else or \fi and processing starts again.

so given \mycommand{This} \mycommand{is}

the two arguments, #1 and #2 of \mycommand are This and \mycommand

the \ifx test is false so processing skips to the \else and the input stream looks like

 \expandafter\eat@white@space\fi{is}


\expandafter expands the \fi so we have

 \eat@white@space{is}


so that expands to

\ifcat is\relax\else is\fi


The catcodes of i and s match so this expands to \relax and the input stream now looks like

\mycommand{my} \mycommand{trial} \mycommand{run.} Testing


Processing goes on as before except this time we get to

 \eat@white@space{trail}


which expands to

\ifcat trial\relax\else is\fi


this time the catcodes of t and r are compared, so it expands to

ial\relax


and ial ends up being typeset as you showed.

-

The macro you are looking for is \unskip. If you change the following two lines

\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\else\unskip\fi}
\newcommand{\mycommandvar}[1]{\ifx\testa\testb{#1}\else\sweetnothing\unskip\fi}


you get the desired result:

## Code:

\documentclass{article}
\makeatletter
%%
\def\testa{a}
\def\testb{b}
\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\else\unskip\fi}
\def\sweetnothing{}
\newcommand{\mycommandvar}[1]{\ifx\testa\testb{#1}\else\sweetnothing\unskip\fi}
%%
\makeatother
\pagestyle{empty}
\begin{document}
\textbf{Line 1:} Testing \mycommand{This} \mycommand{is} \mycommand{my} \mycommand{trial} \mycommand{run.} Testing

\textbf{Line 2:} Testing \mycommandvar{This} \mycommandvar{is} \mycommandvar{my} \mycommandvar{trial} \mycommandvar{run.} Testing

\textbf{Line 3:} Testing {} {} {} {} {} Testing

\textbf{Line 4:} Testing \sweetnothing \sweetnothing \sweetnothing \sweetnothing Testing
\end{document}

-
In addition to my question, "how does this work?" I'm curious, how do I do this in a LaTeX3 context? While some mixing of LaTeX2e and expl3 code is inevitable, it some how seems completely contrary to the spirit of LaTeX3 to then mix TeX commands with LaTeX3 commands. –  A.Ellett Feb 22 '13 at 2:47
\ignorespaces seems to work too. –  A.Ellett Feb 22 '13 at 3:16
Sorry, can't help you with the why, especially since I know almost nothing about expl3. –  Peter Grill Feb 22 '13 at 3:28
That's OK. I'll post that as another question. –  A.Ellett Feb 22 '13 at 3:29
this doesn't remove the space @A.Ellett asked to be removed in the title it removes the preceding space not the trailing one. –  David Carlisle Feb 22 '13 at 12:07