\edef\x trick and pgfmath

With the MWE below, I find the following trouble: within the figure* environment, I have three different \lstset instructions. The first and 3rd (commented out) work, the 2nd (as is) presents with error message " language [ANSI]C undefined". It looks like in the error message the language name is perfect. If I uses the \edef trick, the error goes away. I wonder why in line

\begingroup\edef\x{\endgroup\noexpand\lstset{language={\ME@language}}}\x


I do need \edef, and why 3 lines later

> \ME@decorations % Decorating comments


fails if I handle it in the same way as \ME@language in this line

\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}}
\lstset{language={\ME@language}}
% \begingroup\edef\x{\endgroup\noexpand\lstset{language={\ME@language}}}\x
\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}

#include <stdio.h>

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

• I think you truncated your question May 11, 2015 at 20:37
• The output seems good, but that's because output to the .log may necessarily be expanded. The \edef\x-trick expands it so that it works as expected.
– Werner
May 11, 2015 at 20:44

1 Answer

When you say

\lstset{
language=<whatever>,
}


TeX doesn't expand any macro in <whatever> and listings compares what it got with the known list of languages. In the case <whatever> is \ME@language, it is not in the list.

However, if you do

\begingroup\edef\x{\endgroup\noexpand\lstset{language={\ME@language}}}\x


TeX will eventually do

\lstset{language=[ANSI]C}


and listings will be happy.

The mysterious error message saying that an unknown language was detected is deceiving, because listings passes \ME@language to the error showing command, which will expand it when the message is issued.

• Hmm. I used to design my systems in a way that the error cause and the message informing about that is the same. It is pretty misleading. Thanks for the explanation. May 12, 2015 at 18:38