Cleveref isn't sorting nested lists properly
Turns out @Karalga's explanation of the cause of this bug points in the right direction, but doesn't get to the bottom of the problem. Sorting of nested list labels was broken for two completely independent reasons!
@Karalga's work-around accidentally fixed both of these, without fully understanding why. Unfortunately, I can't use that method in the package, as it depends on the \pdfstreq
internal, which only exists in (sufficiently recent versions of) pdftex
.
The string comparisons in cleveref
use a technique that should work. (It's even one of the techniques explained at that cited stackexchange answer. The comparisons fail in the specific case of enumeration list counter names for reasons that are still somewhat mysterious to me, but which may have something to do with the counter names acquiring strange catcodes.
However, the bigger issue is that LaTeX doesn't use the counter reset list mechanism at all when resetting of enum counters in nested lists! The resetting is hard-coded in the internals of the enumerate
and list
environment definitions. So even if the string comparisons worked, cleveref
still wouldn't be able to detect that enum counters get reset by higher-level enum counters.
Given this, the only reasonable fix is to hard-code enums as special cases in the cref@resetby
macro. (As well as fixing the string comparisons to be catcode-independent.)
I've done this in the latest version (0.20) available from my web site. I'll upload this to CTAN only after I've done some more testing, as this bug was somewhat subtle.
Usually, counters in latex reference their parent counter when being initialised: \newcounter{NameOfTheNewCounter}[NameOfTheOtherCounter]
. This way, the \label
command can access all the parents in the tree structure and their numeric index values. Cleveref makes use of this fact, and notes down the entire structure when the \label
command is used. This is written into the .aux
file. So for Subsection 1.3.4 with label \label{aSubSection}
, this would be:
\newlabel{aSubSection@cref}{{[subsubsection][4][3,1]1.3.4}{1}}
So, when sorting, cleveref just compares the indicies 4, 3, 1
in reverse order. No lexicographical ordering at all - clever cleveref!
Now, you have stumbled on a bug in cleveref. Until a fix is published, here is a workaround:
\documentclass{article}
\usepackage[sort]{cleveref}
\makeatletter
\let\cref@old@resetby\cref@resetby%
\def\cref@resetby#1#2{
\let#2\relax%
\ifnum\pdfstrcmp{#1}{enumii}=\z@
\def#2{enumi}%
\fi%
\ifnum\pdfstrcmp{#1}{enumiii}=\z@
\def#2{enumii}%
\fi%
\ifnum\pdfstrcmp{#1}{enumiv}=\z@
\def#2{enumiii}%
\fi%
\ifnum\pdfstrcmp{#1}{enumv}=\z@
\def#2{enumiv}%
\fi%
\ifx#2\relax%
\cref@old@resetby{#1}{#2}
\fi}%
\makeatother
\begin{document}
\begin{enumerate}
\item foo
\begin{enumerate}
\item 1 \label{1}
\item 2 \label{2}
\begin{enumerate}
\item 5 \label{7}
\item 3 \label{4}
\end{enumerate}
\end{enumerate}
\item bar \label{6}
\begin{enumerate}
\item 3 \label{3}
\begin{enumerate}
\item 5 \label{5}
\end{enumerate}
\end{enumerate}
\end{enumerate}
The reference comes out as: \cref{1,2,3}, \cref{1,2} \cref{2,3}
and \cref{1,2,3,4,5,6,7}. As they should!
\end{document}
The bug is basically an incorrect string comparison function used (testing parameter values with \ifx) - comparing strings in latex can be so hard!
This gives the output: