23

I know how to define a macro that will take the rest of the paragraph as parameter. Simply write \def\a#1\par{\textbf{#1}}.

But, how do I write a macro whose argument will extend to the end of the line?

lockstep
  • 250,273
Yossi Gil
  • 15,951

3 Answers3

17

This answer builds upon Martin Scharrer's updated solution (which was based on mine, based on his... ;-p).

\documentclass{article}
\begin{document}
\newcommand*{\newlinecommand}[2]{%
  \newcommand*{#1}{%
    \begingroup%
    \escapechar=`\\%
    \catcode\endlinechar=\active%
    \csname\string#1\endcsname%
  }%
  \begingroup%
  \escapechar=`\\%
  \lccode`\~=\endlinechar%
  \lowercase{%
    \expandafter\endgroup
    \expandafter\def\csname\string#1\endcsname##1~%
  }{\endgroup#2\space}%
}

%%% USAGE:
\newlinecommand{\emphline}{\emph{#1}}

First words \emphline rest of line
some more text

\end{document}

\emphline sets up the catcode of the end-line character to be active (we could have used a few other choices, as long as it is consistent). It then calls \\emphline, which takes care of grabbing the argument until the end of the line, and applies \emph{ } to it (this is #2 in the definition of \newlinecommand).

We use \begingroup and \endgroup to limit the scope of our change in catcode of \endlinechar to the inside of \emphline.

In order to grab the end of the line, we use a delimited argument, but for this, we need to have an active end-of-line character. Two possibilities:

  • change the catcode locally and then use this active end-of-line in the definition, but this fails in our case, because we are already inside a definition (of \newlinecommand), and catcodes cannot change anymore

  • use \lowercase{~} after defining a lowercase ~ to be the end-of-line character.

Finally, the construction \csname\string#1\endcsname makes the control sequence \\emphline in our case (because #1 is \emphline). We make sure that \string produces \emphline by setting the escape character (that TeX uses for \string) to actually be \.

  • Absolutely brilliant! Maybe this should go into a small package? – Yossi Gil Feb 07 '11 at 06:33
  • @Bruno: Nice solution. Yes, you can modify it to avoid the doubling of #. See my updated example. Also @Yossi. – Martin Scharrer Feb 07 '11 at 07:21
  • @Bruno: Very nice answer! Could you edit it so that the best updated solution is shown first? There is no need to keep a (visible) record of how the answer was improved over time. Editing history is always available by clicking on the link next to “edited”. – Juan A. Navarro Feb 07 '11 at 10:14
  • @Juan: since it was accepted, I assumed that I shouldn't change it without some form of consent from someone. I guess a moderator is enough ;). – Bruno Le Floch Feb 07 '11 at 10:19
  • @Bruno: Nice adaption again. I don't like the name newlinecommand very much because its reads more like a command for newline s. I don't think the code is used so often by many people to justify an own package. Maybe it could be added to an existing package which provides new command definition macros. – Martin Scharrer Feb 07 '11 at 10:22
  • My consent is given of course, if it is required. Not sure I fully understand the intricacies of how to bring the ultimate answer up, and whether I might not be giving the appropriate credit, but I trust you guys to do it right. – Yossi Gil Feb 07 '11 at 10:26
  • @Martin: I just cleaned up my answer above. I agree that \newlinecommand is a bad name. Any better alternative? About packaging, I agree as well. Do you know which packages do this kind of thing? – Bruno Le Floch Feb 07 '11 at 10:40
  • @Bruno: How about \toeolncommand ? – Yossi Gil Feb 07 '11 at 12:34
  • Nice. Is there a way for the command defined by \newlinecommand to also take arguments? – Joachim Breitner Feb 05 '17 at 22:30
  • @JoachimBreitner: It's not trivial and I won't have time in the short-term, sorry. I suggest that you ask a separate question linking to this one, so that someone else can answer you. – Bruno Le Floch Feb 06 '17 at 12:25
  • Is there any alternative method here? I am thinking this because it is difficult for me to maintain your code, for instance here https://tex.stackexchange.com/a/375248/13173 – Léo Léopold Hertz 준영 Jun 22 '17 at 05:23
  • @Bruno, I tried to use this to have a switch for hiding certain table rows in a doc. Basically my idea was to have a \rowswitch in the first cell and define either \newlinecommand{\emphline}{#1} or \newlinecommand{\emphline}{} but I keep getting Forbidden control sequence found while scanning use of \\rowswitch. Is there a way to still achieve this using your method? – sheß Mar 08 '18 at 20:09
  • I made this a separate question (https://tex.stackexchange.com/questions/419220/hide-show-complete-row-of-tabular/419225#419225) and received some really good answers. I just leave this here in case somebody stumbles upon the same question – sheß Mar 09 '18 at 12:37
  • has this been added into a package? – user32882 Apr 19 '22 at 08:08
  • @user32882 I have no idea. Perhaps you can ask a new question, but I don't know if you'll get useful answers. – Bruno Le Floch Apr 20 '22 at 09:57
10

The end-of-line character in TeX is the ASCII 13 Carriage Return character which can be represented by ^^M (M=13th character in the alphabet). This is independent of the actual input file format (i.e. DOS/Windows vs. Unix vs. Mac end-of-line characters).

However you cannot simply write \def\a#1^^M{\textbf{#1}} because TeX doesn't like this. One way is to redefine the catcode (see The TeXBook) of it:

\def\restofline{%
    \begingroup
    \catcode`\^^M=\active
    \irestofline
}
\begingroup
\catcode`\^^M=\active %
\gdef\irestofline#1^^M{%
  \iirestofline{#1}%
  \endgroup %
  \space % Readd effective space from removed end-of-line
}%
\endgroup %

\def\iirestofline#1{\textbf{#1}}

%%% USAGE:
First words \restofline rest of line

This worked in my tests, but might be flawed somehow and there might be better/more efficient ways to do it.

Please note that the end-of-line can be commented out using % in which case this macro will also read the next line.


Here a variation of the solution of Bruno Le Floch (which is based on my solution above) where # in #1 doesn't have to be doubled. The trick is to use a second macro for this. Note that \csname\string#1\endcsname generates a command sequence \\foo from \foo.

\documentclass{article}
\begin{document}
\makeatletter
\newcommand*{\newlinecommand}[1]{%
  \newcommand*{#1}{%
    \begingroup%
    \lccode`\~=\endlinechar%
    \lowercase{\def\restofline@aux####1~}{\endgroup\csname\string#1\endcsname{####1}\space}%
    \catcode\endlinechar=\active%
    \restofline@aux%
  }%
  \expandafter\def\csname\string#1\endcsname##1%
}
\makeatother

%%% USAGE:
\newlinecommand{\emphline}{\emph{#1}}

First words \emphline rest of line
some more text

\end{document}

Please also note that there is a parselines package which might be used. Its code might be a good read for people interested in this kind of parsing.

Martin Scharrer
  • 262,582
6

Here is a crude solution that relies on changing catcodes:

\def\TEST
  {\begingroup
   \catcode`\^^M=13
   \doTEST}

\begingroup
\catcode`\^^M=13
\gdef\doTEST#1^^M%
  {\endgroup The line argument is :#1:}
\endgroup

\TEST this is a command that takes on line
as an argument

some more text

\bye
Aditya
  • 62,301