[Tex/LaTex] How to get autoref working with different counters for different listing environments

counterscross-referencinghyperreflistings

In my document I want several different custom listing environments. The solution from fraktalek works like a charm, except for one thing: \autoref doesn't work for me. I have tried the following:

\newcounter{algorithm}
\lstnewenvironment{algorithm}[1][]{
        \renewcommand\lstlistingname{Algorithm}
        \setcounter{lstlisting}{\value{algorithm}}
        \lstset{#1}
} {\addtocounter{algorithm}{1}}

\newcounter{program}
\lstnewenvironment{program}[1][]{
        \renewcommand\lstlistingname{Program}
        \setcounter{lstlisting}{\value{program}}
        \lstset{#1}
} {\addtocounter{program}{1}}

\def\algorithmautorefname{Algorithm}
\def\programautorefname{Program}

But, in my document,

\autoref{alg:firstalgorithm}, \autoref{prg:firstprogram}, \autoref{alg:secondalgorithm}

returns

Listing 1.1, Listing 1.1, Listing 1.2.

The separate numbering is correct, but autoref does not recognize the separate counters. However, if I use

\def\lstlistingautorefname{Code fragment}

instead, the result changes to

Code fragment 1.1, Code fragment 1.1, Code fragment 1.2.

I have tried putting this command inside the \lstnewenvirontment commands, but this again gave

Listing 1.1, Listing 1.1, Listing 1.2

as a result. Obviously, I would like to result to be

Algorithm 1.1, Program 1.1, Algorithm 1.2.

Any thoughts on how to achieve this?

Best Answer

The solution I'm proposing here is adequate for relatively small documents, but will not work for more complex documents where the user wants to produce something like a \listofalgorithms or \listofprograms. Since this was not specified in the original question, it is not included here. However, I'm sure it is easy to extend it to include such requirements using the float package, for example.

Some background to the solution...

One problem is that the lstlisting environment provided by the listings package is special. It's contents has to be parsed in a way such that nothing is expanded, except for \end{lstlisting}. That's why it cannot be broken up using:

\newenvironment{myenvironment}[1][]{%
  \begin{lstlisting}[#1]% Begin listing
}{\end{lstlisting}}% End listing

giving you a error during compilation. As a consolation, the listings package provides an alternative in the form of

\lstnewenvironment{<name>}[<number>][<opt. default arg.>]{<start code>}{<end code>}

analogous to LaTeX's \newenvironment{nam}[args][opt]{begdef}{enddef} definition. However, merely using this definition as-is in the solution to different counters for different listing environments causes hyperref to complain in the following way

! pdfTeX warning (ext4): destination with the same identifier
(name{page.1}) has been already used, duplicate ignored

This is because the same counter is used - namely lstlisting - across the newly defined listing environments. In fact, they are the same environments, just with different names (due to the command \renewcommand\lstlistingname{<name>}). This warning motives why using

\def\algorithmautorefname{Algorithm}
\def\programautorefname{Program}

does not sway \autoref{...} from using the correct reference title, since \autoref{...} still sees each newly defined environment (algorithm and program) as lstlisting.

One proposed solution would be to add some macros that both prints a correct caption (albeit manually) and correctly hyperlinks to the respective lstlisting. This is done by introducing a "pre-hook" to each new \lstnewenvironment and modifying the way in which parameters are passed to it. Caption and label support via listings' caption={...} and label=... is dropped in lieu of a manual alternative. This way lstlisting environments are always using a incrementally different counter in the background, thereby avoiding the hyperref duplicate destination warning.

\documentclass{article}
\usepackage{listings}
\usepackage{hyperref}

\newcommand{\listingcaption}[2]{%
  \parbox{0.95\textwidth}{% Width of caption is 95% of \textwidth
    \leftskip=0pt plus.5fil% These 3 lines allow for a
    \rightskip=0pt plus-.5fil% justification=centerlast option similar
    \parfillskip=0pt plus1fil% to that offered by the `caption` package
    \small \textbf{#1~\thealgorithm}.\ #2% Caption formatting
  }%
}
% ================== ALGORITHM ==================
\newcounter{algorithm}
%\renewcommand{\thealgorithm}{\thesection.\arabic{algorithm}}% Algorithm counter definition
\newcommand{\algorithmprehook}[2]{%
  \refstepcounter{algorithm}% Increment counter for correct reference
  \listingcaption{Algorithm}{#1}% Algorithm caption
  \label{#2}% Label algorithm
}
\lstnewenvironment{algorithm}[3][]{% \begin{algorithm}[<listings options>]{<caption>}{<label>}...
  \algorithmprehook{#2}{#3}% Algorithm pre-hook
  \lstset{#1}% Set listings options
} {}% ...\end{algorithm}
\def\algorithmautorefname{Algorithm}% Autoref caption

% ================== PROGRAM ==================
\newcounter{program}
%\renewcommand{\theprogram}{\thesection.\arabic{program}}% Program counter definition
\newcommand{\programprehook}[2]{%
  \refstepcounter{program}% Increment counter for correct reference
  \listingcaption{Program}{#1}% Program caption
  \label{#2}% Label program
}
\lstnewenvironment{program}[3][]{% \begin{program}[<listings options>]{<caption>}{<label>}...
  \programprehook{#2}{#3}% Program pre-hook
  \lstset{#1}% Set listings options
} {}% ...\end{program}    
\def\programautorefname{Program}% Autoref caption

\begin{document}
\pagestyle{empty}

\begin{algorithm}[]{My first algorithm. This is an extremely long caption, giving a detailed %
description of the context and code. Justification is ``centerlast''}{alg:firstalgorithm}
  Here is some algorithm code;
  Then some more code;
  And it ends here.
\end{algorithm}

\begin{program}[]{My first program}{prg:firstprogram}
  Here is some program code;
  Which is a little shorter.
\end{program}

\begin{algorithm}[]{My second algorithm}{alg:secondalgorithm}
  The final algorithm code is very short.
\end{algorithm}

\autoref{alg:firstalgorithm}, \autoref{prg:firstprogram}, \autoref{alg:secondalgorithm}

\end{document}

Alternative lstlisting environments (algorithm+program) with correct caption, counter and hyperlinks

The macro \listingcaption{<caption label>}{<caption>}, which provides the manual caption support, takes 2 mandatory arguments. <caption label> is the type of label (Algorithm or Program in this case) and <caption> is the actual caption. The formatting of the caption is similar to the specification

\captionsetup[<float type>]{%
  font=small,%
  format=plain,%
  labelsep=period,%
  labelfont=bf,%
  justification=centerlast%
}

supported by the caption package. The option justification=centerlast was obtained from this recent blog entry, originally suggested by Victor Eijkhout's TeX by Topic.

When using the float package for managing captions, counters and \listof... entries, it is advisable to also forego using the listings options for caption={...} and label=....

Related Question