3

I defined a simple command to comment text inside a paragraph:

\newcommand{\comm}[1]{}

This is a␣\comm{commented text}␣paragraph containing a comment.

The problem is that I get two spaces in place of the comment:

"This is a␣␣paragraph containing a comment."

I could obviously write

This is a\comm{commented text}␣paragraph containing a comment.

but I already have a few comments in my text, and that would also worsen the readability.

Is there any easy (i.e. that does not require much more effort than the trivial solution above) way to show only one space in place of the comment, independently from the spaces before and after the \comm command?

Thank you!

14

Another option would be to use \@bsphack and \@esphack, like this:

\documentclass{article}

\makeatletter %% <- make @ usable in macro names
\newcommand{\comm}[1]{\@bsphack\@esphack}
\makeatother %% <- change @ back

\begin{document}

A Test. \comm{test} This works.

A Test.\comm{test} This works.

A Test. \comm{test}This works.

A Test\comm{test}. This works.

\end{document}

This produces a space of the right length, also if \comm actually does something. This is how for instance \label (which creates a label that can be cross-referenced), \index (which creates an index entry) and \marginpar (which puts text in the margin) work. The way to use \@bsphack and \@esphack in general would be:

\newcommand{\comm}[1]{\@bsphack<do things here>\@esphack}

A little explanation:

\@bsphack and \@esphack do the following (more or less):

  • \@bsphack stores the values of \lastskip and \spacefactor. The first is the length of the preceding space (if applicable, otherwise it is 0), and the second is a number that affects the length of a space at this spot. (E.g., spaces following periods are slightly longer and can be stretched to become a lot longer if necessary.)
  • \@esphack restores \spacefactor and inserts \ignorespaces (which does what it says) if \lastskip was positive when \@bsphack was called.

There are (at least) two limitations: multiple consecutive \@bsphack/\@sphacks don't work, and using this at the end of a paragraph can sometimes cause the last word of this paragraph to be moved to the next line.


I actually use a slightly modified version myself that does allow you to use multiple of these in a row and doesn't have the end-of-paragraph problem. It removes the preceding space and then reinserts it afterwards.

\documentclass{article}

\newcommand{\comm}[1]{\mybsphack\myesphack}
%% This also works if \comm does do something:
% \newcommand{\comm}[1]{\mybsphack{\marginpar{#1}}\myesphack}

\makeatletter              %% <- make @ usable in macro names
\newskip\my@savsk          %% <- holds a skip
\newcount\my@savsf         %% <- holds space factor
\newcount\my@savpn         %% <- holds penalty (for line breaking)
\newcommand*{\mybsphack}{% %% <- this % is relevant, see the comments
  \relax\ifhmode           %% <- if in horizontal mode
    \my@savsk\lastskip     %% <- store \lastskip
    \@savsf\spacefactor    %% <- store \spacefactor
    \my@savpn\lastpenalty  %% <- store \lastpenalty
    \unskip                %% <- remove the last skip
  \fi
}
\newcommand*{\myesphack}{% %% <- this % is relevant, see the comments
  \relax\ifhmode           %% <- if in horizontal mode
    \penalty\my@savpn      %% <- restore the penalty
    \spacefactor\@savsf    %% <- restore the space factor
    \ifdim\my@savsk>\z@    %% <- if the removed skip was positive
      \hskip\my@savsk      %% <- then reinsert it
      \ignorespaces        %% <- and ignore subsequent spaces
    \fi
  \fi
}
\makeatother               %% <- change @ back

\begin{document}

A Test. \comm{test}\comm{test}This works.

A Test.\comm{test}\comm{test} This works.

A Test. \comm{test}\comm{test} This works.

A Test. \comm{test} \comm{test}This works.

A Test.\comm{test} \comm{test} This works.

A Test. \comm{test} \comm{test} This works.

A Test\comm{test}\comm{test}. This works.

\end{document}

enter image description here

Edit: If \comm is supposed to truly do nothing, then the following would suffice:

\newcommand{\comm}[1]{\ifdim\lastskip>0pt\ignorespaces\fi}

Edit: Improved my code: it now takes into account penalties and stores \lastskip in a skip register rather than the default dimension register (oops).

  • Off topic: I often see lines ending with % (not a comment), what is that for? – Taekwondavide May 12 '18 at 1:47
  • 1
    I added a % to comment out the newline at the end of these lines because TeX reads single newlines as spaces, and inserting a space there would kind of defeat the purpose of the hack :). See this answer for more information. – Circumscribe May 12 '18 at 8:12
  • Not every line needs this % because spaces directly following control words (a \ followed by letters, like \ifhmode) are already ignored (as are spaces following the lengths for \ifdim and \hskip, also if they hadn't been control words). When writing your own macros it is probably best to err on the side of caution when you're unsure, since adding an unnecessary % generally causes no harm. (Spaces at the start of a line are also ignored btw, and spaces in macros used in math mode are fine because they don't do anything.) – Circumscribe May 12 '18 at 8:24
  • @Circumscribe note that there are cases where a superfluous % can do harm, e.g. if you use something that looks for a number (like \ifnum) and the next line starts with something that could be interpreted as a number. – Skillmon May 12 '18 at 10:26
  • @Skillmon Good point. I guess the correct advice would be that you should be somewhat careful with this. – Circumscribe May 12 '18 at 11:03
6

You could use \ignorespaces:

\documentclass[]{article}

\newcommand\comm[1]{\ignorespaces}

\begin{document}
Foo \comm{bar} baz
\end{document}

This eats the space after \comm but not the one before it. As a results, this will only work properly, if you use it with one space before it.

If you only use \comm mid-sentence and only in between two words (so where a space should appear), you could use:

\newcommand\comm[1]{\ifhmode\unskip\fi\space\ignorespaces}

Which would result in one space, ignoring any trailing spaces and eating a leading space. I'd go with the first option and use it correctly, this way you don't get any unexpected behaviour, which could happen if you use \unskip at the wrong place.

  • 1
    safer to use \ifhmode\unskip\fi in the second version – David Carlisle May 11 '18 at 18:44
  • @DavidCarlisle good idea, thanks! It still seems like a not so versatile version. – Skillmon May 11 '18 at 18:55
  • 1
    \@bpshack ... \@esphack is the full flavoured version – David Carlisle May 11 '18 at 19:01
  • @DavidCarlisle I don't know these, feel free to post an answer. – Skillmon May 11 '18 at 19:13
  • 2
    they are the "space hacks" used around \label and \index and similar commands in the latex sources (take care of spacefactor and other things) but in a document command I'd be more likely to use one of yours so I'll just leave the comment, interested parties can look in latex.ltx :-) – David Carlisle May 11 '18 at 19:21

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.