12

My custom enumerateoptional enumitem environment is putting a useless empty line in the middle of my enumeration list.

To reproduce this, you need to fill a line 100%. See this example I extracted from my full document, reproducing the problem:

\documentclass[12pt]{article}
\errorcontextlines 10000
\usepackage[shortlabels]{enumitem}

\def\renewenumerateoptionalitem{%
  \let\itemenumerateoptional\item%
  \renewcommand{\item}[1][]{%
      \refstepcounter{enumerateoptionali}% increment the counter
      \itemenumerateoptional[\bfseries##1~\theenumerateoptionali]%
  }%
}

\newlist{enumerateoptional}{enumerate}{1}
\setlist[enumerateoptional]{
    before=\renewenumerateoptionalitem,
    label=\arabic*,
    nosep,
    align=left,
    leftmargin=*,
    after=\let\item\itemenumerateoptional,
}

\usepackage{hyperref}
\begin{document}
\begin{enumerateoptional}[1.]
    \item[\bfseries test\_duplicatedContext] Detecção de contextos duplicados e
    emissão de um erro semântico.
    \item[\bfseries test\_duplicatedIncludes] Detecção de inclusões duplicadas  e
    emissão de um erro semântico.
    \item[\bfseries test\_invalidRegexInput] Detecção de expressões regulares 
    inválidas e emissão de um erro semântico.
    \item[\bfseries test\_missingIncludeDetection] Detecção da inclusão um bloco 
    inexistente.
    \item[\bfseries test\_duplicatedGlobalNames] Detecção de múltiplas definições 
    do nome da gramática e
    emissão de um erro semântico.
    \item[\bfseries test\_missingScopeGlobalName] Detecção da falta da definição do 
    nome do escopo global da gramática e emissão de um erro semântico.
\end{enumerateoptional}
\end{document}

On the following image, the extra new line is between the 4th and 5th item. It is happening because the 4th line is filled 100% the maximum width and latex is putting a extra new line right after it I do not know why.

enter image description here

How can I stop this useless empty line popping up in the middle of my lists, depending on each item line size?

user
  • 4,745
  • 2
    Adding an empty line after that item removes the spurious empty line. I'm not sure why that happens, though... Should be something related to the fact that \item does a \par eventually, but apparently at the time it does it's too late. Adding the \par (blank line) earlier works... – Phelype Oleinik Oct 21 '19 at 03:40
  • what about the accented letters? maybe some package calculates the dimensions of an \item including the accented letters, but the typeset example doesn't inlcude them. Does the output look the same if you replace the text in the source code by Deteco da incluso um bloco inexistente.? – thymaro Oct 21 '19 at 03:46
  • @thymaro, with or without the accented letter, the problem happens the same way. On my full document, I got the accented latter correctly displayed. – user Oct 21 '19 at 03:52
  • @PhelypeOleinik, Indeed, I added a empty line before each \item and it was fixed. Thanks! – user Oct 21 '19 at 03:55
  • ok, good, just wanted to make sure it hasn't got to do with that. If you just add lines between your items, the bug is not solved, just ignored. You may move past it, now that it's small and inoccuous, but keep in mind that it might come back later in an evolved state (think Godzilla) to bite your heels (or swallow you whole). – thymaro Oct 21 '19 at 04:26
  • 5
    Really puzzling... I narrowed the issue down to the hyperref package only. There is something that is making this weird behaviour. Add a \par after the item and the problem goes away. Add a % after the item and the problem also goes away. Comment out the \refstepcounter line and the problem goes away. Comment out hyperref and, you guessed, the problem goes away. Looks like a bug (or at least a quite odd feature) to me... – Phelype Oleinik Oct 21 '19 at 04:54

1 Answers1

18

The blank line you see is a misfeature of your code and hyperref making a PDF destination (when you use \refstepcounter) at that exact point.

The workaround of adding a \par after the text works because the paragraph is built and then the PDF destination is inserted at the beginning of the next line and everything goes well. Adding the PDF destination at the end of the line may cause the destination to go to the next (as far as TeX can see) and then the line seems empty.

An arguably better workaround would be to add \par at the beginning of the definition of your modified \item command, so that you make sure that the PDF destination is inserted in the correct place.

However the correct way would be to attach the destination to the \item itself, so that the PDF destination will always be in the right place. One good bad place to insert it, is right before printing the value of the counter. As Ulrike said in the comment, the label is typeset in a box (by default), and the \@currentlabel is lost (I assumed, incorrectly, that its assignment was global as the counter).

Here's a reworked version which uses a patched version of \@item which uses \refstepcounter in the right place, and uses a few more enumitem

\documentclass[10pt]{article}
\usepackage[shortlabels]{enumitem}
\usepackage{etoolbox}
\makeatletter
\let\user@item\@item
\patchcmd\user@item{\if@noitemarg}{\iftrue}{}{\FAILED}
\enitkv@key{}{formatarg}{\def\enit@format##1{#1}}
% Compatibility with older enumitem.sty:
\@ifundefined{enitkv@enumitem@formatarg}
  {\enitkv@key{enumitem}{formatarg}{\def\enit@format##1{#1}}}{}
\newlist{enumerateoptional}{enumerate}{1}
\setlist[enumerateoptional]{%
    before=\let\@item\user@item,
    formatarg=\textbf{########1~\@itemlabel},
    nosep,
    align=left,
    leftmargin=*,
}
\makeatother
\usepackage[colorlinks]{hyperref}
\begin{document}
\begin{enumerateoptional}[1.]
  \item[test\_duplicatedContext] Detecção de contextos duplicados e
    emissão de um erro semântico.
  \item[test\_duplicatedIncludes] Detecção de inclusões duplicadas  e
    emissão de um erro semântico.
  \item[test\_invalidRegexInput] Detecção de expressões regulares 
    inválidas e emissão de um erro semântico.
  \item[test\_missingIncludeDetection]\label{this} Detecção da inclusão um bloco 
    inexistente.
  \item[test\_duplicatedGlobalNames] Detecção de múltiplas definições 
    do nome da gramática e
    emissão de um erro semântico.
  \item[test\_missingScopeGlobalName] Detecção da falta da definição do 
    nome do escopo global da gramática e emissão de um erro semântico. Ver item~\ref{this}.
\end{enumerateoptional}
\end{document}

enter image description here

Also there is no need to restore \item in the after code (it doesn't harm either) because the \let you do is local to the environment, so once it ends \item is restored to its original definition.

You also don't need \bfseries in every single \item: you already use \bfseries in the new definition of \item.

  • 2
    Adding \refstepcounter{enumerateoptionali} inside the optional argument doesn't work: this is a box and you loose the @currentlabel and the destination for links. Imho a really sane solution would need a change to the latex @item command so that it allows to increase the counter even if an optional argument is present (or accepts a second optional argument). – Ulrike Fischer Oct 21 '19 at 09:25
  • @UlrikeFischer I assumed the assignment to \@currentlabel was global too. I fixed it. Thanks :-) – Phelype Oleinik Oct 21 '19 at 15:08
  • @PhelypeOleinik, after you latest update, the example image is missing the dot . after the number (remember the enumitem shortlabel [1.] I passed when instantiating the list on \begin{enumerateoptional}[1.]). Also, latex cannot build you example anymore. It throws: main.tex:20: Package enumitem Error: formatarg undefined. I tested both with MikTek and TexLive 2017. – user Oct 22 '19 at 00:53
  • @user Oops. Fixed it to take the label into account. The error formatarg undefined is probably because you a) didn't copy the line that says \enitkv@key{}{formatarg}{\def\enit@format##1{#1}}, or b) forgot to write \makeatletter before it. The key formatarg is not defined by enumitem: I defined it to be a small variation of the format key. – Phelype Oleinik Oct 22 '19 at 09:59
  • @PhelypeOleinik, I copied you whole example into a 100% clean new document. I recorded a video proving it to you: https://i.imgur.com/D2xTY5e.gif – user Oct 22 '19 at 22:49
  • @user A link to the .log file would've been enough to spot the problem. You're on TeXLive 2016, and apparently the \enitkv@key{}{formatarg}{\def\enit@format##1{#1}} wasn't available at the time, for some reason... I'll check and report back. (One rule about posting answers here is: never trust OP. That's why I asked :-) – Phelype Oleinik Oct 22 '19 at 23:18
  • @user With older versions of enumitem, you had to do \enitkv@key{enumitem}{formatarg}{...}, now the enumitem in the first argument is the default and shouldn't be used, that's why I had \enitkv@key{}{formatarg}{...}. I updated to work with either version. – Phelype Oleinik Oct 22 '19 at 23:30
  • @PhelypeOleinik, using your latest solution, I got an error while using \setlist*[enumerateoptional]{any option}. If I try your first solution, everything works fine with \setlist*[enumerateoptional]{...}. I opened another question asking for a fix: Illegal parameter number in definition of \enit@c after using \setlist*[enumerateoptional]{nosep} – user Nov 03 '19 at 04:43