2

I have a macro that makes a \cline of user-specified thickness. I want to create a * and un-* version to account for the difference between two cases depending on whether there is more than one \cline in a sequence (e.g., \cline{1-2}\cline{5-6}). (When the \cline rules are thick, they can clobber part/all of content of the row beneath unless there's a correcting \vskip at the end.)

The unstarred version adds this \vskip at the end, and you'd use it for the last \cline of a sequence (which subsumes the case where it's the only \cline). The starred version omits this \vskip, and you'd use it for all but the final \cline in a sequence. In the below code, \ClineTStar is that starred macro without the \vskip correction. I then define \ClineTNoStarto simply call \ClineTStar and add the correcting \vskip at the end.

These differently named versions of the macro are working fine, when I call them with their distinct names. See the output below. The starred \clines after the row starting with A & … undesirably clobber the row underneath (that should start with I & J & K…). But the \clines after row Q & R… (which is two starred followed by one unstarred macro) properly leave room for the next row's content Y & Z…

But I'd like to call this pair of macros as \ClineT* and \ClineT. When I follow the traditional \@ifstar approach to create these flavors, and call either version, I get an error message like:

Misplaced \omit. \@ClineTStar #1-#2#3\@nil ->\omit \@multicnt #1\advance \@multispan \m@ne \i... l.36 \ClineT*{1-2}{10pt} \ClineT*{4-5}{10pt} \ClineT{7-8}{10pt}

I've commented out the line \ClineT*{1-2}{10pt} \ClineT*{4-5}{10pt} \ClineT{7-8}{10pt} so that it will compile.

What am I doing wrong? (I don't want to use LaTeX3 methods, because, AFAIK, that's not compatible with Overleaf v1. So I'd like to fix this \@ifstar approach.)

\documentclass{article}
\makeatletter
\def\ClineTStar#1#2{\@ClineTStar#1#2\@nil}
\def\@ClineTStar#1-#2#3\@nil{%
    \omit
    \@multicnt#1%
    \advance\@multispan\m@ne
    \ifnum\@multicnt=\@ne\@firstofone{&\omit}\fi
    \@multicnt#2%
    \advance\@multicnt-#1%
    \advance\@multispan\@ne
    \leaders\hrule\@height#3\hfill
    \cr
    \noalign{\vskip-#3}%
}
\makeatother
\newcommand{\ClineTNoStar}[2]{%
    \ClineTStar{#1}{#2}%
    \noalign{\vskip#2}%
}

\makeatletter
\newcommand{\ClineT}{\@ifstar{\ClineTStar}{\ClineTNoStar}}
\makeatother

\begin{document}
\begin{tabular}{| c | c | c | c | c | c | c | c |}
\hline
A & B & C & D & E & F & G & H \\
\ClineTStar{1-2}{10pt} \ClineTStar{4-5}{10pt} \ClineTStar{7-8}{10pt}
I & J & K & L & M & N & O & P \\
Q & R & S & T & U & V & W & X \\
\ClineTStar{1-2}{10pt} \ClineTStar{4-5}{10pt} \ClineTNoStar{7-8}{10pt}
Y & Z & A & B & C & D & E & F \\
%\ClineT*{1-2}{10pt} \ClineT*{4-5}{10pt} \ClineT{7-8}{10pt}
E & F & G & H & I & J & K & L \\
\hline
\end{tabular}
\end{document}

enter image description here

8
  • Unrelated to your question: I don’t think your implementation will work well with \ClineTStar{9-11}{10pt}. Commented Sep 4, 2018 at 1:41
  • @RuixiZhang, it doesn't like the corresponding \cline{9-11} either. I'm happy if this works in the cases that \cline accepts. Commented Sep 4, 2018 at 2:58
  • The original \@cline has two markers: Everything that comes before - is #1; everything after - but before \@nil is #2. It breaks down in your code since #2 can only be one token. Commented Sep 4, 2018 at 3:05
  • @RuixiZhang, what I was referring to is that {9-11} means "span from column 9 through column 11." In my example, there are only 8 columns. Neither my macro nor \cline works gracefully here when you specify an out-of-bounds column range. Mine seems to work fine for column ranges that are feasible. Commented Sep 4, 2018 at 3:32
  • 1

1 Answer 1

4

The problem here is that \omit really must be the first command that appears in the tabular cell after \\. If you use macros there, they have to be fully expandable and \omit has to be the very first token of that expansion. As \@ifstar uses several assignments before it can make the decision if the next token is *, it will fail here. So we have to replace \ClineT by a fully expandable macro:

\documentclass{article}
\makeatletter
\def\ClineTStar#1#2{\@ClineTStar#1#2\@nil}
\def\@ClineTStar#1-#2#3\@nil{%
    \omit
    \@multicnt#1%
    \advance\@multispan\m@ne
    \ifnum\@multicnt=\@ne\@firstofone{&\omit}\fi
    \@multicnt#2%
    \advance\@multicnt-#1%
    \advance\@multispan\@ne
    \leaders\hrule\@height#3\hfill
    \cr
    \noalign{\vskip-#3}%
}
\makeatother
\newcommand{\ClineTNoStar}[2]{%
    \ClineTStar{#1}{#2}%
    \noalign{\vskip#2}%
}

\makeatletter
\def\ClineT#1{%
    \expandafter\ifx\@firstofmany#1\relax\@end*%
        \expandafter\ClineTStar
    \else
        \ClineT@#1%
    \fi
}
\def\ClineT@#1\fi{\fi\ClineTNoStar{#1}}
\def\@firstofmany#1#2\@end{#1}
\makeatother

\begin{document}
\begin{tabular}{| c | c | c | c | c | c | c | c |}
\hline
A & B & C & D & E & F & G & H \\
\ClineTStar{1-2}{10pt} \ClineTStar{4-5}{10pt} \ClineTStar{7-8}{10pt}
I & J & K & L & M & N & O & P \\
Q & R & S & T & U & V & W & X \\
\ClineTStar{1-2}{10pt} \ClineTStar{4-5}{10pt} \ClineTNoStar{7-8}{10pt}
Y & Z & A & B & C & D & E & F \\
\ClineT*{1-2}{10pt} \ClineT*{4-5}{10pt} \ClineT{7-8}{10pt}
E & F & G & H & I & J & K & L \\
\hline
\end{tabular}
\end{document}

enter image description here

\ClineT takes one argument that will be either * or a group like {1-2}. We find the first token of that argument using the helper macro \*firstofmany and compare it against *. If they are the same, \ClineTStar is the next thing to be called.

A bit trickier is the other branch because we need to re-apply the already read argument to \ClineTNoStar. Simply \else\ClineTNoStar{#1}\fi won't work—now \fi would become the second argument of \ClineTNoStar. The solution is to use a helper macro that uses \fi as delimited parameter, immediately puts it back to the token stream and calls the actual macro as the final part of the expansion.

2
  • 1
    A beautiful answer. +1 Commented Sep 4, 2018 at 2:48
  • That certainly fixes it! This is area of TeX knowledge that I know little about, so it's somewhat Greek to me, but I'm very happy to have the solution. Commented Sep 4, 2018 at 2:50

You must log in to answer this question.

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