4

If one is in a tabular environment, how can one make sure, that the previous line has ended (with \\)?

One might want to add an \hline or do some other stuff only at the beginning of a new line.

If one adds \\ before, that might add an unwanted, additional empty line.

So the question is, in comparison to \clearpage vs. \newpage, if there is a likewise command for tabular \??? vs. \\?

Werner
  • 603,163
Toscho
  • 4,713
  • 3
    This question looks like an “X-Y” problem (ask about Y when you are actually trying to achieve X), so I’m not going to answer it; but as a hint, recall that immediately after a \\ TeX is in vertical mode, and this condition can be tested with \ifvmode. – GuM Jul 05 '17 at 18:15

3 Answers3

4

The following introduces a tabular newline macro \tnl which checks whether it's followed by \tnl using \@ifnextchar. Since \@ifnextchar leaves the "peeked token" in the input stream, concatenated called to \tnl are all ignored, except for the last one:

enter image description here

\documentclass{article}

\makeatletter
\newcommand{\tnl}{\@ifnextchar\tnl{}{\\}}
\makeatother

\begin{document}

\begin{tabular}{ l }
  one \\ two
\end{tabular}\quad
\begin{tabular}{ l }
  one \tnl two
\end{tabular}

\bigskip

\begin{tabular}[t]{ l }
  one \\ \\ two
\end{tabular}\quad
\begin{tabular}[t]{ l }
  one \tnl \tnl two
\end{tabular}

\bigskip

\begin{tabular}[t]{ l }
  one \\ \\ \\ two
\end{tabular}\quad
\begin{tabular}[t]{ l }
  one \tnl \tnl \tnl two
\end{tabular}

\end{document}

This does remove the optional argument specification that accompanies \\. Not sure whether that's needed.

Werner
  • 603,163
  • This command \tnl seems to do the same a \crcr in David Carlisle's answer (as far as I can see). And it yields the same result in my other question ( https://tex.stackexchange.com/questions/378324/how-to-make-ifdefempty-on-datatool-entries-work-in-longtabu/ ). – Toscho Jul 05 '17 at 17:59
3

Mostly you can use the TeX primitive \crcr to achieve this. \crcr acts like \cr unless the previous command was \cr in which case it does nothing.

This is the heart of the reason that latex can ignore a \\ at the end of the final row.

the only wrinkle is that if you end the previous row with \crcr then it as if it was ended with \cr rather than \\ but in most cases this comes to the same thing.

GuM
  • 21,558
David Carlisle
  • 757,742
  • Thx for the hint on \crcr, but this does not work as I expect it in my other question ( https://tex.stackexchange.com/questions/378324/how-to-make-ifdefempty-on-datatool-entries-work-in-longtabu/ ). If I add \crcr before the \hline in the commented line, the code compiles without erros, but adds an empty line. – Toscho Jul 05 '17 at 17:57
  • @Toscho well that's not surprising:-) \crcr only does nothing if there are no non-expandable commands since the last \cr the chance that that's the case in the middle of that loop is small. You would be better to put either \\ or \\\hline at the beginning of each row, at the point you can test \bar – David Carlisle Jul 05 '17 at 18:04
3

Just to illustrate the suggestion I made in a comment:

% My standard header for TeX.SX answers:
\documentclass[a4paper]{article} % To avoid confusion, let us explicitly 
                                 % declare the paper format.

\usepackage[T1]{fontenc}         % Not always necessary, but recommended.
% End of standard header.  What follows pertains to the problem at hand.

\newcommand*{\ensureLineHasEnded}{\ifvmode \else \expandafter \\\fi}
\newcommand*{\NLhline}{\ensureLineHasEnded\hline}



\begin{document}

\noindent
\begin{tabular}{|l|}
    \hline
    A one-column \texttt{tabular}, just for demo purposes.  \\
    \NLhline
    It uses both vertical and horizontal rules (arrggh\ldots!).  \\
    \NLhline
    But some of the \verb|\\|~commands are missing\ldots
    \NLhline
    \ldots yet the horizontal rules are typeset normally.  \\*[6pt]
    \NLhline
    The \verb|\ensureLineHasEnded| can take the same optional arguments\ldots
    \ensureLineHasEnded*[6pt]\hline
    \ldots as the \verb|\\|~command.
    \NLhline
    Moreover, it works as expected when used at the end of the \texttt{tabular}.
    \ensureLineHasEnded[9pt]
\end{tabular}\par

\end{document}

Edit: As David Carlisle has remarked, the \ifvmode test does not work if p-type columns are used, because TeX is in vertical mode in these columns too. Note that, more precisely, TeX is internal vertical mode both inside p-type columns and between the lines of an alignment (The TeXbook, p. 282), so a supplementary \ifinner test wouldn’t help either. A distinction that does work in simple situations is based on the current group level, which can be enquired about by means of the e-TeX primitive \currentgrouplevel.

The following code is just a proof of concept: there’s little point in refining it unless you know the precise context into which it’s going to be used.

% My standard header for TeX.SX answers:
\documentclass[a4paper]{article} % To avoid confusion, let us explicitly 
                                 % declare the paper format.

\usepackage[T1]{fontenc}         % Not always necessary, but recommended.
% End of standard header.  What follows pertains to the problem at hand.

\makeatletter

\@ifdefinable\@tabular@group@level{\newcount\@tabular@group@level}
\newcommand*\ensureLineHasEnded{%
    \ifnum \currentgrouplevel>\@tabular@group@level
        % % Diagnostics:
        % \count@ \currentgrouplevel
        % \typeout{Inserting \protect\\ at line \number\inputlineno,
        %     since \number\count@\space > \number\@tabular@group@level}%
        \expandafter \\%
    \fi
}
\newcommand*\saltAwayLevel{%
    \noalign{%
        \global \@tabular@group@level
            \numexpr \currentgrouplevel -\@ne \relax
        % % Diagnostics:
        % \typeout{\number\@tabular@group@level}
    }%
}

\makeatother

\newcommand*{\NLhline}{\ensureLineHasEnded\hline}



\begin{document}

With \texttt{l}~columns:
\begin{center}
    \begin{tabular}{|l|}
        \saltAwayLevel
        \hline
        A one-column \texttt{tabular}, just for demo purposes.  \\%
        \NLhline
        It uses both vertical and horizontal rules (arrggh\ldots!).  \\
        \NLhline
        But some of the \verb|\\|~commands are missing\ldots
        % \typeout{\number\currentgrouplevel}%
        \NLhline
        \ldots yet the horizontal rules are typeset normally.  \\*[6pt]
        \NLhline
        The \verb|\ensureLineHasEnded| can take the same optional arguments\ldots
        \ensureLineHasEnded*[6pt]\hline
        \ldots as the \verb|\\|~command.  \\
        \NLhline
        Moreover, it works as expected when used at the end of the \texttt{tabular}.
        \ensureLineHasEnded[9pt]
    \end{tabular}\par
\end{center}

\bigbreak

With \texttt{p}~columns:
\begin{center}
    \begin{tabular}{|p{.9\linewidth}|}
        \saltAwayLevel
        \hline
        A one-column \texttt{tabular}, just for demo purposes.  \\%
        \NLhline
        It uses both vertical and horizontal rules (arrggh\ldots!).  \\
        \NLhline
        But some of the \verb|\\|~commands are missing\ldots
        % \typeout{\number\currentgrouplevel}%
        \NLhline
        \ldots yet the horizontal rules are typeset normally.  \\*[6pt]
        \NLhline
        The \verb|\ensureLineHasEnded| can take the same optional arguments\ldots
        \ensureLineHasEnded*[6pt]\hline
        \ldots as the \verb|\\|~command.  \\
        \NLhline
        Moreover, it works as expected when used at the end of the \texttt{tabular}.
        \ensureLineHasEnded[9pt]
    \end{tabular}\par
\end{center}

\end{document}

You can uncomment the diagnostic commands to get additional information on what is going on.

GuM
  • 21,558
  • 2
    yes I wondered about suggesting that but that only works if the otherwise bad \hline is in an l column, if it would be in a p column the vmode test would (or at least could) show as true anyway – David Carlisle Jul 05 '17 at 19:03
  • 1
    Thank you for the answer, but it still doesn't work, if I use it in a \DTLforeach from datatool-package, that produces one line ending with \\ per iteration. It seems, that \DTLforeach does something, which breaks all these checks, before going through the iterated code, although it doesn't print something. – Toscho Jul 05 '17 at 19:47
  • 1
    @Toscho: Indeed, as D.C. has remarked the test is not robust enough. I intend to remove this answer after the usual grace period of 48 hours that I apply, by policy, to all my pieces of nonsense. :-) – GuM Jul 05 '17 at 19:54
  • @GuM: would you consider leaving the answer up? As you say, it’s a slightly flawed answer, but (a) it still works for some use-cases, so may be useful to some readers; (b) other answerers may be able to build on the idea and improve it to something more robust; and (c) having it here together with the explanation of its limitations is useful to anyone else who thinks of using this approach. – Peter LeFanu Lumsdaine Jul 05 '17 at 20:17