使用 pgfkeys 将参数传递给 lstinputlisting

使用 pgfkeys 将参数传递给 lstinputlisting

我正在编写一个宏来将代码和图形并排放置。有三个相关文件:main.texfigure.texch1.tex

main.tex大致是这样的:

...
\usepackage{listings}
...
\lstdefinestyle{base}{
...
}
\lstdefinestyle{C}{
    style=base,
    language=C,
}
...
\input{figure}
...
\begin{document}
...
\include{ch1}
...
\end{document}

figure.tex定义我的宏,它有(除其他外)这个:

....
\usepackage{pgfkeys}
\def\figpath{./figs/}
\def\codepath{./code/}
\makeatletter
...
% set of keys and default values for group "fig"
\pgfkeys{/codefig/entities/.cd,
  code-file/.initial=DUMMY.txt,
  code-width/.initial=0.5,
  code-opts/.initial={style=Plain},
  fig-file/.initial=DUMMY,
  fig-page/.initial=1,
  fig-width/.initial=0.5,
  fig-inner-width/.initial=0.8,
  caption/.initial={},
  label/.initial={},
}

% practical "setter" and "getter" functions
\def\codefig@set@keys#1{\pgfkeys{/codefig/entities/.cd,#1}}
\def\codefig@get#1{\pgfkeysvalueof{/codefig/entities/#1}}

\newcommand\CodeFigure[1]{%%
\bgroup%%
\codefig@set@keys{#1}%%
\vspace{\figAir}
\begin{figure}[htbp]\centering%%
\begin{minipage}[b]{\codefig@get{code-width}\textwidth}
\lstinputlisting[\codefig@get{code-opts}]{\codepath\codefig@get{code-file}}
\centering (a)
\end{minipage}%%
\begin{minipage}[b]{\codefig@get{fig-width}\textwidth}
\centering\includegraphics%%
    [width=\codefig@get{fig-inner-width}\textwidth,page=\codefig@get{fig-page}]%%
    {\figpath\codefig@get{fig-file}}\\%%
\centering (b)
\end{minipage}%%
\caption{{\codefig@get{caption}}}%%
\label{\codefig@get{label}}%%
\end{figure}%%
\vspace{\figAir}
\egroup%%
}

\makeatother

最后,ch1.tex打算这样使用这个宏:

\CodeFigure{
  code-file={my-code.c},
  code-width=0.6,
  code-opts={style=C},
  fig-file=the-fig-filename,
  fig-page=2,
  fig-width=0.4,
  fig-inner-width=0.6,
  caption={Some Nice Caption},
  label={some-label},
} %% this is line 643

然而,Latex 却对我大喊:

./ch1.tex:643: Package keyval Error: style=C undefined.

我猜想“name=value”字符串没有被正确解析,但我不知道如何实现这一点。如果我更改该行:

\lstinputlisting[\codefig@get{code-opts}]{\codepath\codefig@get{code-file}}

为了这:

\lstinputlisting[style=C]{\codepath\codefig@get{code-file}}

它按预期工作。但是,硬编码选项“不是一个选择”因为我需要在文档的不同部分传递不同的内容。

答案1

扩展控制在这里确实是关键。您将只希望\lstinput看到传入的值,而不是整个值\codefig@get(因为底层keyval解析器看不到要拆分的等号或逗号,并且认为整个值是一个键,在错误消息输入期间,该值将被扩展,因此您会看到style=C错误)。

下面定义了另一个辅助函数\codefig@eget,它将完全扩展\pgfkeysvalueof使用\romannumeral扩展(所以如果一个值有一个被吞噬的前导空格,它将继续扩展该值,直到它到达第一个不可扩展的标记 - 至少在您的使用中这不应该是一个问题,如果事实证明这是一个问题,您可以依赖实现\pgfkeysvalueof来始终需要已知数量的扩展步骤,尽管这可能会在未来发生变化,因此可能是一个坏主意),辅助函数始终需要恰好两个步骤来扩展为值,并保护其结果免于在或\expand上下文中进一步扩展\edef

我删除了\vspace你放在figure环境周围的 s,那些是错误的。它们figure可能会浮动到另一个地方,在这种情况下,你会\vspace在文档中间放置两个 s,从而导致出现奇怪的不必要的空间。如果你想控制浮动元素周围的间距,有多种方法,请搜索网站。

我还删除了周围的组figure,而是将键解析移到其中,因为它figure形成了自己的组,并且您不需要它外面的值。

\figpath也不是个好主意,你可以改用/\graphicspath命令。我仍然将你的留在代码中。graphicsgraphicx\figpath

\documentclass{article}

\usepackage{listings}
\usepackage{graphicx}
\usepackage{pgfkeys}
\def\figpath{}
\def\codepath{./}

\makeatletter
% set of keys and default values for group "fig"
\pgfkeys{/codefig/entities/.cd,
  code-file/.initial=DUMMY.txt,
  code-width/.initial=0.5,
  code-opts/.initial={style=Plain},
  fig-file/.initial=DUMMY,
  fig-page/.initial=1,
  fig-width/.initial=0.5,
  fig-inner-width/.initial=0.8,
  caption/.initial={},
  label/.initial={},
}

% practical "setter" and "getter" functions
\def\codefig@set@keys#1{\pgfkeys{/codefig/entities/.cd,#1}}
\def\codefig@get#1{\pgfkeysvalueof{/codefig/entities/#1}}
\def\codefig@eget#1%
  {%
    \unexpanded\expandafter
      {\romannumeral`\^^@\pgfkeysvalueof{/codefig/entities/#1}}%
  }

\newcommand\CodeFigure[1]
  {%
    \begin{figure}[htbp]%
      \codefig@set@keys{#1}%
      \centering
      \begin{minipage}[b]{\codefig@get{code-width}\textwidth}%
        \expanded{\noexpand\lstinputlisting[{\codefig@eget{code-opts}}]}%
          {\codepath\codefig@get{code-file}}
        \centering (a)
      \end{minipage}%
      \begin{minipage}[b]{\codefig@get{fig-width}\textwidth}%
        \centering
        \includegraphics
          [{width=\codefig@get{fig-inner-width}\textwidth,page=\codefig@get{fig-page}}]%
          {\figpath\codefig@get{fig-file}}\\
        (b)
      \end{minipage}%%
      \caption{\codefig@get{caption}\label{\codefig@get{label}}}%
    \end{figure}%%
  }
\makeatother

\begin{document}
\CodeFigure{
  code-file={\jobname.tex},
  code-width=0.6,
  code-opts={language=[LaTeX]TeX,firstline=1,lastline=4},
  fig-file=example-image-duck,
  fig-page=2,
  fig-width=0.4,
  fig-inner-width=0.6,
  caption={Some Nice Caption},
  label={some-label},
}
\end{document}

替代方法:不要使用pgfkeys但是(方便,因为可以通过...expkv-cs直接访问各个键,而不必关心扩展),以及(用于定义具有预定义键类型的键的接口,类似于处理程序方法,尽管定义和设置在单独的命令中)。#1#9\ekvcSplitexpkv-defpgfkeys

另外,我没有在所有键前面加上或作为前缀fig-,而是code-简单地定义了两个包装器键来设置选项。

\documentclass{article}

\usepackage{listings}
\usepackage{graphicx}
\usepackage{expkv-cs,expkv-def}
\def\figpath{}
\def\codepath{./}

\makeatletter
\ekvcSplit\codefig@code
  {
     file  = DUMMY.txt
    ,width = .5
    ,opts  = style=Plain
  }
  {%
    \begin{minipage}[b]{#2\textwidth}%
      \lstinputlisting[{#3}]{\codepath#1}
      \centering (a)
    \end{minipage}%
  }
\ekvcSplit\codefig@fig
  {
     file  = DUMMY
    ,page  = 1
    ,width = .5
    ,inner-width = .8
  }
  {%
    \begin{minipage}[b]{#3\textwidth}%
      \centering
      \includegraphics
        [{width=#4\textwidth,page=#2}]%
        {\figpath#1}\\
      (b)
    \end{minipage}%%
  }

\ekvsetdef\codefig@set@keys{codefig@keys}
\ekvdefinekeys{codefig@keys}
  {%
     dataT label   = \codefig@label
    ,dataT caption = \codefig@caption
    ,data  code    = \codefig@codekeys
    ,data  fig     = \codefig@figkeys
  }
\newcommand\codefig@caption@aux[1]{\caption{#1\codefig@label\label}}

\newcommand\CodeFigure[1]
  {%
    \begin{figure}[htbp]%
      \centering
      \codefig@set@keys{#1}%
      \codefig@codekeys\codefig@code{\codefig@code{}}%
      \codefig@figkeys\codefig@fig{\codefig@fig{}}%
      \codefig@caption\codefig@caption@aux
    \end{figure}%%
  }
\makeatother

\begin{document}
\CodeFigure{
  code = {
    file={\jobname.tex},
    width=0.6,
    opts={language=[LaTeX]TeX,firstline=1,lastline=4},
  },
  fig = {
    file=example-image-duck,
    page=2,
    width=0.4,
    inner-width=0.6,
  },
  caption={Some Nice Caption},
  label={some-label},
}
\end{document}

两者的结果:

在此处输入图片描述

相关内容