![Actualización y decoraciones de tikz/pgf.](https://rvso.com/image/309882/Actualizaci%C3%B3n%20y%20decoraciones%20de%20tikz%2Fpgf..png)
Desde una actualización importante en pgf, mi código ya no se compila. Estoy intentando hacer una lista de fuentes "decorada", donde las decoraciones pueden ser como resaltar algunas líneas (ver MWE), bolas en algunas líneas, imágenes, etc. Las decoraciones son opcionales y las paso como argumentos macro. Aquí va mi 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}
y el gran código que quiero decorar
#include <stdio.h>
int main()
{
printf("hello, world\n");
}
La intención del código es poner un cuadro resaltado alrededor de las líneas 1-3. El código, tal como está, funciona. Es sólo para demostrar que la macro \MEHighlightLines funciona bien. Ahora puede comentar la última línea anterior con esa macro y descomentar la línea que contiene la misma macro 3 líneas encima.
Ahora recibes el mensaje de error.
! Undefined control sequence.
\pgfmathsetcount ... \pgfmath@onquick #2\pgfmath@
{\afterassignment \pgfmath...
Dado que la única diferencia es que ahora la macro se pasa como argumento, en lugar de llamarse directamente, supongo que la causa está oculta en la expansión de la macro en el nuevo pgf. El método de paso de argumentos es
\begingroup\edef\x{\endgroup\noexpand\lstinputlisting[label=#4, name=#4] {#2}
{\ME@decorations} % Decorating comments
}\x
El error está en alguna parte al expandir mis macros pasadas en el pgf actualizado. (Las decoraciones deben mantenerse junto con la lista, por lo que las fuerzo en una figura \. También se aceptan ideas de implementación alternativas (como solución alternativa), pero la mejor manera sería corregir el error de expansión de la macro).
Respuesta1
Bueno, el error es bastante claro: el código
\begingroup\edef\x{\endgroup\noexpand\lstinputlisting[label=#4, name=#4] {#2}
{\ME@decorations} % Decorating comments
}\x
debiera ser
\lstinputlisting[label=#4, name=#4] {#2}
\ME@decorations % Decorating comments
No hay absolutamente ninguna razón para usar el \edef\x
truco aquí y el problema es exactamente que \ME@decorations
no sobrevive \edef
porque contiene asignaciones (por ejemplo \pgfmathtruncatemacro
).
Por cierto, también {\begin{figure*}[htbp]}
con {\end{figure*}}
está mal y \lstset
bien puede ir tras \begin{figure*}
, por lo que el entorno \begingroup
se \endgroup
vuelve superfluo.
Aquí hay una versión arreglada (y reformateada).
\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}