10

In the MWE, \xspace correctly inserts a space in the first test of \foo, but not in the second test when it is followed by an en-dash.

Is there a fix for this?

\documentclass{article}
\usepackage{xspace}
\newcommand{\foo}{FOO\xspace}

\begin{document}
\foo BAR

\foo -- why no space here?

\end{document}

Output:

enter image description here

3
  • 5
    Note the question is misworded, xspace never detects the space, the space is not available to tex macros at all. so it detects B in the first case and - in the second. for B it guesses a space is needed so adds one, for - it guesses it is a hyphenated word, so does not. The input \foo -- is identical to the input \foo-- after tex parsing, neither generates a space token. Commented Oct 3, 2018 at 11:18
  • @DavidCarlisle Question tItle edited!
    – alephzero
    Commented Oct 3, 2018 at 11:28
  • see also tex.stackexchange.com/questions/180686/… Commented Oct 3, 2018 at 11:29

1 Answer 1

16

The hyphen character - is in xspace's exception list1, where the space isn't added. You can remove it with \xspaceremoveexception{-}:

\documentclass{article}
\usepackage{xspace}
\newcommand{\foo}{FOO\xspace}

\begin{document}
\foo BAR

\foo -- why no space here?

\xspaceremoveexception{-}
\foo -- why no space here?

\end{document}

enter image description here


As David said in the comments, xspace doesn't detect the space. As far as it knows \foo--bar and \foo --bar are exactly the same, because at the time \xspace gets to do its thing, TeX has already tokenized the macro and the next character, and any space in between was ignored.

xspace looks ahead to see if the next character is supposed to be preceded by a space and re-inserts that space if it is needed.

With the solution above, we changed \xspace's exception list, so it will always, from now on, add a space before a -, even when you don't want it to.

Still not satisfied?

If you want to hack into \xspace's rules, you can start by the example given in the manual:

\documentclass{article}
\usepackage{xspace}
\newcommand{\foo}{FOO\xspace}

\begin{document}
\foo BAR

\foo -- why no space here?

\xspaceremoveexception{-}
\foo -- why no space here?

\xspaceremoveexception{-}% repeated, for the sake of copy-pasting
\makeatletter
\renewcommand*\@xspace@hook{%
  \ifx\@let@token-%
    \expandafter\@xspace@dash@i
  \fi
}
\def\@xspace@dash@i-{\futurelet\@let@token\@xspace@dash@ii}
\def\@xspace@dash@ii{%
  \ifx\@let@token-%
  \else
    \unskip
  \fi
  -%
}
\makeatother

\foo - why no space here?

\foo -- why no space here?

\end{document}

enter image description here

There David shows how to use \@xspace@hook to check if the next two characters are -- and, if not, remove the space inserted by \xspace. This can be expanded to check more characters2, but is it worth it?

My 2 cents: I used xspace for a few months, when I was writing my bachelor's thesis. It helps you not forget to type \foo{} or \foo\ when you are in the habit of forgetting it. But with time, you need different things (as you did here), and it gets more time consuming than it should. My opinion (and David's, apparently :P).


1 The exception list, by default, contains:

,.'/?;:!~-)\ \/\bgroup\egroup\@sptoken\space\@xobeysp\footnote\footnotemark
%        ↑ Here it is :)

2 Apparently I've got nothing better to do :)

The \@xspace@hook below checks:

  1. If what follows \foo is a single -. If it is, it removes the space inserted by \xspace and returns;
  2. If what follows \foo is --. If it is, it looks ahead for a space. If the space is found, the space inserted by \xspace is kept, otherwise it is removed. That is:

    \foo-bar  % prints FOO-bar
    \foo-- bar% prints FOO -- bar
    \foo--bar % prints FOO--bar
    

(note that either way, the space after \foo does not matter because of TeX's parsing rules).

enter image description here

\documentclass{article}
\usepackage{xspace}
\newcommand{\foo}{FOO\xspace}

\begin{document}
\pagenumbering{gobble}
\foo BAR

\foo -- why no space here?

\xspaceremoveexception{-}
\foo -- why no space here?

\xspaceremoveexception{-}
\makeatletter
\renewcommand*\@xspace@hook{%
  \begingroup
  \ifx\@let@token-%
    \obeyspaces
    \expandafter\@xspace@dash@i
  \fi
}
\def\@xspace@dash@i-{\futurelet\@let@token\@xspace@dash@ii}
\def\@xspace@dash@ii{%
  \ifx\@let@token-%
    \expandafter\@xspace@dash@sp@i
  \else
    \unskip
    -%
    \endgroup
  \fi
}
\def\@xspace@dash@sp@i-{\futurelet\@let@token\@xspace@dash@sp@ii}
\def\@xspace@dash@sp@ii{%
  \ifx\@let@token\space%
  \else
    \unskip
  \fi
  --%
  \endgroup
}
\makeatother

\foo - why no space here?

\foo -- why no space here?

\foo--why no space here?

\end{document}
6
  • all true but you could also make clear that xspace never detects the space after \foo so if you do this \foo--BAR would also make FOO --BAR with a space being inserted by xspace. Commented Oct 3, 2018 at 11:20
  • But removing - from the exception list messes up using a single - as a hyphen character. Oh well, I suppose I can't have everything working the way I want it ;)
    – alephzero
    Commented Oct 3, 2018 at 11:23
  • @alephzero my recommendation would be to not use xspace at all tex.stackexchange.com/questions/86565/drawbacks-of-xspace/… Commented Oct 3, 2018 at 11:24
  • OK, I'll consider throwing \newcommand away as well, and using \def\foo/{FOO} with a guard character to prevent accidents. (\foo -- is now invalid, but \foo/-- and \foo/ -- both do what you would expect.
    – alephzero
    Commented Oct 3, 2018 at 11:34
  • @DavidCarlisle Done. Slightly wordier version :) Commented Oct 3, 2018 at 11:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .