5

I found this limitation in the current tikzmark solution for highlighting lines of listings code in beamer. When I put the whole lot in an onlyenv environment, it seems the marks are not set, or found. Example:

\documentclass{beamer}
\usepackage{tikz}
\usepackage{listings}
\usetikzlibrary{tikzmark,fit}
\usetikzmarklibrary{listings}
\newcommand{\showmark}[2]{%
  \tikz[remember picture,overlay]{\draw[red,thick] ({pic cs:line-#1-#2-first}) -- ({pic cs:line-#1-#2-end});}%
}
\begin{document}
\begin{frame}[fragile]
\begin{onlyenv}<1>
\showmark{first}{2}
\begin{lstlisting}[name=first]
Listing
In first slide
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<2>
\showmark{second}{2}
\begin{lstlisting}[name=second]
Listing
In second slide
\end{lstlisting}
\end{onlyenv}
\end{frame}
\end{document}

I see a red line in the first slide, but not in the second. Is there any (simple) workaround for this problem? Or am I doing something wrong?

PS. The \showmark macro is just a quick way of showing the problem.

Jellby
  • 3,323

1 Answers1

5

Since you don't restart the value for the lines, the second line in the second listing is actually the fourth line; either restart the lines or use the proper value:

\documentclass{beamer}
\usepackage{tikz}
\usepackage{listings}
\usetikzlibrary{tikzmark,fit}
\usetikzmarklibrary{listings}
\newcommand{\showmark}[2]{%
  \tikz[remember picture,overlay]{\draw[red,thick] ({pic cs:line-#1-#2-first}) -- ({pic cs:line-#1-#2-end});}%
}
\begin{document}
\begin{frame}[fragile]
\begin{onlyenv}<1>
\showmark{aaa}{2}
\begin{lstlisting}[name=aaa]
Listing
In first slide
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<2>
\showmark{second}{4}
\begin{lstlisting}[name=second]
Listing
In second slide
\end{lstlisting}

\end{onlyenv}
\end{frame}
\end{document}

enter image description here

Under normal conditions, using \resetcounteronoverlays (or \resetcountonoverlays for a TeX counter) would take care of preventing a counter from stepping on overlays; however, when the name=<name> option is used for a listing, the values for the line numbers are stored inside \lstno@<name> which is a macro and not a counter, so \resetbeameronoverlays will not work and there's no native way to prevent the undesired behaviour. One can manually set the counter to the appropriate value, or set explicitly the appropriate number for firstnumber, as shown below:

\documentclass{beamer}
\usepackage{tikz}
\usepackage{etoolbox}
\usepackage{listings}
\usetikzlibrary{tikzmark,fit}
\usetikzmarklibrary{listings}
\newcommand{\showmark}[2]{%
  \tikz[remember picture,overlay]{\draw[red,thick] ({pic cs:line-#1-#2-first}) -- ({pic cs:line-#1-#2-end});}%
}

\resetcounteronoverlays{lstnumber}

\begin{document}

\begin{frame}[fragile]
\begin{onlyenv}<1>
\showmark{first}{2}
\begin{lstlisting}[name=first]
Listing
In first slide
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<2>
\showmark{second}{2}
\begin{lstlisting}[name=second,firstnumber=1]
Listing
In second slide
\end{lstlisting}
\end{onlyenv}
\end{frame}

\end{document}

or

\documentclass{beamer}
\usepackage{tikz}
\usepackage{etoolbox}
\usepackage{listings}
\usetikzlibrary{tikzmark,fit}
\usetikzmarklibrary{listings}
\newcommand{\showmark}[2]{%
  \tikz[remember picture,overlay]{\draw[red,thick] ({pic cs:line-#1-#2-first}) -- ({pic cs:line-#1-#2-end});}%
}

\resetcounteronoverlays{lstnumber}

\begin{document}

\begin{frame}[fragile]
\begin{onlyenv}<1>
\showmark{first}{2}
\begin{lstlisting}[name=first]
Listing
In first slide
\end{lstlisting}
\end{onlyenv}
\makeatletter
\def\lstno@second{1}
makeatother
\begin{onlyenv}<2>
\showmark{second}{2}
\begin{lstlisting}[name=second]
Listing
In second slide
\end{lstlisting}
\end{onlyenv}
\end{frame}

\end{document}

Related to this problem is Problem with listings when using line numbers, `name`, and `beamer` overlays. I initially though about manually resetting \lst@firstnumber, but as Andrew Swann explains in his answer, it's better to consider \lstno@<name>.

Gonzalo Medina
  • 505,128
  • Thanks, that helps. I tried resetting the line numbers, because that would make each piece of code more self-contained. But when I add firstnumber=1 to both lstlisting options, I get the opposite behavior: red line in the second slide and not in the first. Besides, the manual seems to imply that by using the name option the lines start at 1... Could you provide an example resetting the line numbers? – Jellby Mar 11 '14 at 08:43
  • @Jellby unfortunately, the native beamer solution (\resetcounteronoverlays) won't work when name is used (the reason is included in my edited answer), so there's no "natural way to prevent the undesired behaviour". I provided a work-around in my edited answer. – Gonzalo Medina Mar 12 '14 at 12:33
  • Thanks, and do you know why adding firstnumber=1 to the first listing doesn't work? It works for all of them, even in other frames, except for the first (at least in my tests). – Jellby Mar 12 '14 at 13:22
  • @Jellby I also noticed that. I didn't dig enough into the code of listings, so I don't know the reason. – Gonzalo Medina Mar 12 '14 at 13:25
  • @Jellby I updated my answer with new information that might be of interest for you. – Gonzalo Medina Mar 12 '14 at 17:52
  • Thanks a lot for this. However, it doesn't work if the overlay specification are out of order (try with <1> first, then <3>, and then <2>). This may be needed, for example, when using the onlyenv inside a columns environment: The left column may contain <1-2> and <3>, and the right column <2>. – Jellby Mar 24 '15 at 11:57