2

Since a major upgrade in pgf, my code does not compile any more. I am trying to make "decorated" source listing, where the decorations can be like highlighting some lines (see the MWE), balls on some lines, images, etc. The decorations are optional, and I pass them as macro arguments. Here goes my MWE

    \documentclass{book}
    \usepackage{xcolor}
    \usepackage{xkeyval}
    \usepackage{listings}
    \usepackage{tikz}   
\usetikzlibrary{shapes,positioning,arrows,fit,backgrounds,calc,tikzmark,shadows}%

    \definecolor{ForestGreen}{rgb}{0.0, 0.4, 0.0}
    \definecolor{burntorange}{rgb}{0.8, 0.33, 0.0}
    \newlength{\mywidth} % Used in defining listing width
    \definecolor{ivory}{rgb}{1.0, 1.0, 0.94}
        \colorlet{SeparatorColor}{burntorange}

        \makeatletter
        \define@key{MEMacros}{decorations}{\def\ME@decorations{#1}}
        \define@key{MEMacros}{language}{\def\ME@language{#1}}
        \define@key{MEMacros}{number}{\def\ME@number{#1}}
        \presetkeys{MEMacros}{number=1}{}%
    \makeatother


    %%Usage \MESourceFile[keys]{source file}{caption}{label}
    \makeatletter
    \newcommand\MESourceFile[4][]{
    \setkeys{MEMacros}{ %language={[ANSI]C},
     decorations={},#1}% 
          \begingroup%
            \lstset{    language={[ANSI]C}      }   

             {\begin{figure*}[h!btp] }
             \caption{#3}
            \begingroup\edef\x{\endgroup\noexpand\lstinputlisting[label=#4, name=#4] {#2}
                {\ME@decorations} % Decorating comments
                }\x
            {\end{figure*}}
      \endgroup%
    }
    \makeatother
    % % Prepare for some decorations on the listing files
        \makeatletter
        \newif\iflst@linemark

        \lst@AddToHook{EveryLine}{%
     \begingroup
     \advance\c@lstnumber by 1\relax
     \pgfmark{line-\lst@name-\the\c@lstnumber-start}%
     \endgroup
        }

        \lst@AddToHook{EOL}{\pgfmark{line-\lst@name-\the\c@lstnumber-end}%
        \global\lst@linemarktrue
        }

        \lst@AddToHook{OutputBox}{%
     \iflst@linemark
     \pgfmark{line-\lst@name-\the\c@lstnumber-first}%
     \global\lst@linemarkfalse
     \fi
        }

        \def\tkzlst@fnum#1\relax#2\@STOP{%
     \def\@test{#2}%
     \ifx\@test\@empty
     \def\tkzlst@start{0}%
     \else
     \@tempcnta=#1\relax
     \advance\@tempcnta by -1\relax
     \def\tkzlst@start{\the\@tempcnta}%
     \fi
        }

        \lst@AddToHook{Init}{%
     \expandafter\tkzlst@fnum\lst@firstnumber\relax\@STOP
     \pgfmark{line-\lst@name-\tkzlst@start-start}%
        }

    % % Put a balloon around some lines in a source
    % Usage: \MEHighlightLines{BallonName}{SourceName}{FirstLine}{LastLine}
    \newcommand\MEHighlightLines[4]{%
      \pgfmathtruncatemacro\pgf@temp{%
       #3-1
      }%
      \iftikzmark{line-#2-\pgf@temp-start}{%
       \iftikzmark{line-#2-#3-first}{%
         \xdef\b@lines{({pic cs:line-#2-\pgf@temp-start} -| {pic           cs:line-#2-#3-first})}%
       }{%
         \iftikzmark{line-#2-#3-start}{%
           \xdef\b@lines{({pic cs:line-#2-\pgf@temp-start} -| {pic             cs:line-#2-#3-start})}%
         }{%
           \xdef\b@lines{(pic cs:line-#2-\pgf@temp-start)}%
         }%
       }%
      }{%
       \xdef\b@lines{}%
      }%
      \foreach \k in {#3,...,#4} {%
       \iftikzmark{line-#2-\k-first}{%
         \xdef\b@lines{\b@lines (pic cs:line-#2-\k-first) }
       }{}
       \iftikzmark{line-#2-\k-end}{%
         \xdef\b@lines{\b@lines (pic cs:line-#2-\k-end) }
       }{}
      }%
      \ifx\b@lines\pgfutil@empty
      \else
      \edef\pgf@temp{\noexpand\tikz[remember picture,overlay]\noexpand\node[fit={\b@lines}, color=ForestGreen,yshift=-2pt,
         draw, fill=green!30, opacity=0.4,  inner sep=1pt, rounded corners=5pt] (#1) {};
      }%
            \pgf@temp
      \fi
            }
    \makeatother

    \begin{document}
    \MESourceFile[decorations={
    %\MEHighlightLines{HelloWorldsystem}{lst:HelloWorld.c}{1}{3}
    }
    ]{HelloWorld.c}{Hello world}{lst:HelloWorld.c}
    \MEHighlightLines{HelloWorldsystem}{lst:HelloWorld.c}{1}{3}
    \end{document}

and the great code I want to decorate

#include <stdio.h>

int main()
{
     printf("hello, world\n");
}

The intention of the code is to put a highlighted box around lines 1-3. The code, as is, works. It is just to demonstrate that \MEHighlightLines macro works OK. Now you can comment out the pre-last line with that macro and uncomment the line containing the same macro 3 lines above it.

Now you receive the error message

! Undefined control sequence.
\pgfmathsetcount ... \pgfmath@onquick #2\pgfmath@ 
                                                  {\afterassignment \pgfmath...

Since the only difference is that now the macro is passed as argument, rather then called directly, I guess that the cause is hidden in expanding the macro in the new pgf. The argument passing method is

\begingroup\edef\x{\endgroup\noexpand\lstinputlisting[label=#4, name=#4] {#2}
    {\ME@decorations} % Decorating comments
    }\x

The error is somewhere in expanding my passed macros in the upgraded pgf. (The decorations must be kept together with the listing, and so do I force them in a \figure. Alternative implementation ideas also welcome (as a workaround), but the best way would be to fix the macro expansion bug.)

2

Well, the error is quite clear: the code

\begingroup\edef\x{\endgroup\noexpand\lstinputlisting[label=#4, name=#4] {#2}
{\ME@decorations} % Decorating comments
}\x

should be

\lstinputlisting[label=#4, name=#4] {#2}
\ME@decorations % Decorating comments

There's absolutely no reason for using the \edef\x trick here and the problem is exactly that \ME@decorations doesn't survive \edef because it contains assignments (for instance \pgfmathtruncatemacro).

By the way, also {\begin{figure*}[htbp]} with {\end{figure*}} is wrong and \lstset can well go after \begin{figure*}, so the surrounding \begingroup and \endgroup become superfluous.

Here's a fixed (and reformatted) version.

\documentclass{book}
\usepackage{xcolor}
\usepackage{xkeyval}
\usepackage{listings}
\usepackage{tikz}   
\usetikzlibrary{
  shapes,
  positioning,
  arrows,
  fit,
  backgrounds,
  calc,
  tikzmark,
  shadows,
}

\definecolor{ForestGreen}{rgb}{0.0, 0.4, 0.0}
\definecolor{burntorange}{rgb}{0.8, 0.33, 0.0}
\newlength{\mywidth} % Used in defining listing width
\definecolor{ivory}{rgb}{1.0, 1.0, 0.94}
\colorlet{SeparatorColor}{burntorange}

\makeatletter
\define@key{MEMacros}{decorations}{\def\ME@decorations{#1}}
\define@key{MEMacros}{language}{\def\ME@language{#1}}
\define@key{MEMacros}{number}{\def\ME@number{#1}}
\presetkeys{MEMacros}{number=1}{}%
\makeatother

%%Usage \MESourceFile[keys]{source file}{caption}{label}
\makeatletter
\newcommand\MESourceFile[4][]{%
  \setkeys{MEMacros}{%
    %language={[ANSI]C},%
    decorations={},%
    #1%
  }%
  \begin{figure*}[h!btp]
  \lstset{language={[ANSI]C}}
  \caption{#3}
  \lstinputlisting[label=#4, name=#4]{#2}%
  \ME@decorations % Decorating comments
  \end{figure*}
}
%% Prepare for some decorations on the listing files
\newif\iflst@linemark

\lst@AddToHook{EveryLine}{%
  \begingroup
  \advance\c@lstnumber by \@ne
  \pgfmark{line-\lst@name-\the\c@lstnumber-start}%
  \endgroup
}

\lst@AddToHook{EOL}{%
  \pgfmark{line-\lst@name-\the\c@lstnumber-end}%
  \global\lst@linemarktrue
}

\lst@AddToHook{OutputBox}{%
  \iflst@linemark
    \pgfmark{line-\lst@name-\the\c@lstnumber-first}%
    \global\lst@linemarkfalse
  \fi
}

\def\tkzlst@fnum#1\relax#2\@STOP{%
  \def\@test{#2}%
  \ifx\@test\@empty
    \def\tkzlst@start{0}%
  \else
    \@tempcnta=#1\relax
    \advance\@tempcnta by \m@ne
    \def\tkzlst@start{\the\@tempcnta}%
  \fi
}

\lst@AddToHook{Init}{%
  \expandafter\tkzlst@fnum\lst@firstnumber\relax\@STOP
  \pgfmark{line-\lst@name-\tkzlst@start-start}%
}

%% Put a balloon around some lines in a source
% Usage: \MEHighlightLines{BallonName}{SourceName}{FirstLine}{LastLine}
\newcommand\MEHighlightLines[4]{%
  \pgfmathtruncatemacro\pgf@temp{#3-1}%
  \iftikzmark{line-#2-\pgf@temp-start}
    {%
     \iftikzmark{line-#2-#3-first}
       {%
        \xdef\b@lines{({pic cs:line-#2-\pgf@temp-start} -| {pic cs:line-#2-#3-first})}%
       }
       {%
        \iftikzmark{line-#2-#3-start}
          {%
           \xdef\b@lines{({pic cs:line-#2-\pgf@temp-start} -| {pic cs:line-#2-#3-start})}%
          }
          {%
           \xdef\b@lines{(pic cs:line-#2-\pgf@temp-start)}
          }%
       }%
    }
    {%
     \xdef\b@lines{}%
    }%
  \foreach \k in {#3,...,#4} {%
     \iftikzmark{line-#2-\k-first}
       {%
         \xdef\b@lines{\b@lines (pic cs:line-#2-\k-first) }
       }
       {}%
       \iftikzmark{line-#2-\k-end}
         {%
          \xdef\b@lines{\b@lines (pic cs:line-#2-\k-end) }
         }
         {}%
  }%
  \ifx\b@lines\pgfutil@empty
  \else
    \edef\pgf@temp{%
      \noexpand\tikz[remember picture,overlay]
      \noexpand\node[
        fit={\b@lines}, color=ForestGreen,yshift=-2pt,
        draw, fill=green!30, opacity=0.4,  inner sep=1pt, rounded corners=5pt
      ] (#1) {};
    }%
    \pgf@temp
  \fi
}
\makeatother

\begin{document}

\MESourceFile[%
  decorations={
    \MEHighlightLines{HelloWorldsystem}{lst:HelloWorld.c}{1}{3}
  }%
]{HelloWorld.c}{Hello world}{lst:HelloWorld.c}

\end{document}

enter image description here

  • Thanks a lot!(It looks like I balanced myself: in question tex.stackexchange.com/questions/197224/… I did not use \edef, although I should, here I did, although I should not. :)) – katang May 11 '15 at 19:06
  • BTW: in the cited question, you suggested to use \begingroup\edef\x{\endgroup \noexpand\lstset{language={\pb@language}}}\x, so that is why I used the same trick for \ME@decorations. Can you please give me a link, to understand the difference between the two cases? (the MWE was over-simplified) – katang May 11 '15 at 19:12
  • @katang That's a very different setting, where an expandable macro is concerned. – egreg May 11 '15 at 19:38
  • I still have problem: your solution works with that constant setting, but not with my argument like \begin{figure*}[h!btp] % \lstset{language={[ANSI]C}} \lstset{language={\ME@language}} \caption{#3} \lstinputlisting[label=#4, name=#4]{#2}% \ME@decorations % Decorating comments \end{figure*} – katang May 11 '15 at 19:39
  • @katang Ask a new question, please – egreg May 11 '15 at 19:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.