[Tex/LaTex] algorithm2e + cleveref: problem with wrong reference to line numbers

algorithm2ecleverefcross-referencing

For some reason I do not understand, cleverefproduces wrong references to line numbers of listings produces with the algorithm2e package. I hope that I did not miss a simple solution …

My example is the following:

\documentclass[paper=A4, fontsize=11pt]{scrbook}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage[linesnumbered]{algorithm2e}
\usepackage{hyperref}
\usepackage{cleveref}

\begin{document}

\begin{algorithm}
Do something\;
Do something else\;\label{line}
\end{algorithm}

line~\ref{line} vs.\ \cref{line}.
\end{document}

The output (which you can also check online) is

Example output

The \ref command works as expected (and refers to line 2) but the \cref command always refers to line 1 (no matter how many lines there are and which line I try to reference).

Does anybody know a solution to this problem?

Best Answer

You found an oversight in the cleveref package. First, algorithm2e numbers the lines with the AlgoLine counter, and cleveref does not track that. So we need a

\crefalias{AlgoLine}{line}

to get that sorted.

Next, cleveref hacks into the \label command to write another label for its own use. In your example, the .aux contains

\newlabel{line}{{3}{1}{}{AlgoLine.1.3}{}}
\newlabel{line@cref}{{[line][1][]1}{1}}

The second line is written by cleveref. The offending part here is {[line][1][]1}, which is actually the content of \cref@currentlabel. Why is it wrong? While \label looks at \@currentlabel, cleveref uses its own \cref@currentlabel to determine what is been labeled. In order to keep that up to date, cleveref hacks into \refstepcounter, and updates \cref@currentlabel there. This works fine when algorithm2e is used without hyperref, but when hyperref is also loaded, the AlgoLine counter is updated with \stepcounter, and cleveref does not hack into that! This also means that cleveref will fail on all counters that are not (only) manipulated with \refstepcounter.

The solution is to also hack into \stepcounter, and update \cref@currentlabel there, too. The code for doing so is the same as for \refstepcounter. The complete code is then

\documentclass[paper=A4, fontsize=11pt]{scrbook}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage[linesnumbered]{algorithm2e}
\usepackage{hyperref}
\usepackage{cleveref}

\crefalias{AlgoLine}{line}%

\makeatletter
\let\cref@old@stepcounter\stepcounter
\def\stepcounter#1{%
  \cref@old@stepcounter{#1}%
  \cref@constructprefix{#1}{\cref@result}%
  \@ifundefined{cref@#1@alias}%
    {\def\@tempa{#1}}%
    {\def\@tempa{\csname cref@#1@alias\endcsname}}%
  \protected@edef\cref@currentlabel{%
    [\@tempa][\arabic{#1}][\cref@result]%
    \csname p@#1\endcsname\csname the#1\endcsname}}
\makeatother

\begin{document}
\begin{algorithm}
Do something\;
Do more\;
Do something else\;\label{line}
\end{algorithm}

line~\ref{line} vs.\ \cref{line}
\end{document}