在宏中使用 lstlisting 失败:列表开始后文本丢失

在宏中使用 lstlisting 失败:列表开始后文本丢失

下面我的第一个代码示例运行良好。它根据是否\flag定义进行条件编译。

\documentclass{article}
\usepackage{listings}
\begin{document}

\ifdefined\flag
\begin{lstlisting}
foo
\end{lstlisting}
\else
\begin{lstlisting}
bar
\end{lstlisting}
\fi

\end{document}

现在我尝试像这样为条件编译创建一个宏。

\documentclass{article}
\usepackage{listings}
\def\flag{}
\newcommand{\ifflag}[2]{\ifdefined\flag#1\else#2}
\begin{document}

\ifflag{
\begin{lstlisting}
foo
\end{lstlisting}
}{
\begin{lstlisting}
bar
\end{lstlisting}
}

\end{document}

编译像这样失败。

Package Listings Warning: Text dropped after begin of listing on input line 15.

在第二个例子中我做错了什么?

答案1

\UDcollectverbarg我可以提供具有以下语法的宏:

\UDcollectverbarg{⟨^^M-replacement⟩}{⟨Mandatory 1⟩}{⟨Mandatory 2⟩}⟨verbatimized argument⟩

得出的结果是:

⟨Mandatory 1⟩{⟨Mandatory 2⟩{⟨verbatimized argument⟩}}

,其中表示行尾的每个字符^^M都被标记序列替换⟨^^M-replacement⟩

Mandatory-arguments 是强制性的。如果它们由多个标记组成,则必须将它们嵌套在 catcode-1/2 字符对/括号中。
如果需要读取和标记化,这将在未改变的类别代码制度下进行。verbatim
-Arg 也是强制性的。它将在逐字类别代码制度下读取和标记化。如果它的第一个字符是括号,则将“假定”该参数嵌套在括号中。否则,将假定该参数的结尾由第一个字符分隔 - 就像 的参数一样\verb
空行不会被忽略。

我选择这种语法是因为通过这种语法,您可以通过嵌套调用第一个强制参数来收集第二个强制参数中的逐字\UDcollectverbarg参数\UDcollectverbarg

例如,

\UDcollectverbarg{<^^M-replacement>}%
                 {\UDcollectverbarg{<^^M-replacement>}{\UDcollectverbarg{<^^M-replacement>}{<actionA>}}}% <- Mandatory 1
                 {<actionB>}%                     <- Mandatory 2
                 <verbatimized argument 1><verbatimized argument 2><verbatimized argument 3>

产量:

\UDcollectverbarg{<^^M-replacement>}{\UDcollectverbarg{<^^M-replacement>}{<actionA>}}% <- Mandatory 1
                 {<actionB>{<verbatimized argument 1>}}%        <- Mandatory 2
                 <verbatimized argument 2><verbatimized argument 3>

产量:

\UDcollectverbarg{<^^M-replacement>}{<actionA>}% <- Mandatory 1
                 {<actionB>{<verbatimized argument 1>}{<verbatimized argument 2>}}% <- Mandatory 2
                 <verbatimized argument 3>

产量:

<actionA>{<actionB>{<verbatimized argument 1>}{<verbatimized argument 2>}{<verbatimized argument 3>}}

假设<actionA>= \@firstofone

\@firstofone{<actionB>{<verbatimized argument 1>}{<verbatimized argument 2>}{<verbatimized argument 3>}}

产量:

<actionB>{<verbatimized argument 1>}{<verbatimized argument 2>}{<verbatimized argument 3>}

\documentclass{article}

\makeatletter

%%<-------------------- Code for \UDcollectverbarg -------------------->
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\expandafter\@firstoftwo{ }{}%
  \@secondoftwo}{\expandafter\expandafter\@firstoftwo{ }{}\@firstoftwo}%
}%
%%......................................................................
\begingroup
\catcode`\^^M=12 %
\@firstofone{%
  \endgroup%
  \newcommand\UDEndlreplace[2]{\romannumeral0\@UDEndlreplace{#2}#1^^M\relax{}}%
  \newcommand*\@UDEndlreplace{}%
  \long\def\@UDEndlreplace#1#2^^M#3\relax#4#5{%
    \UD@CheckWhetherNull{#3}%
    { #5{#4#2}}{\@UDEndlreplace{#1}#3\relax{#4#2#1}{#5}}%
  }%
}%
\newcommand\UDcollectverbarg[3]{%
  \begingroup
  \let\do\@makeother % <- this and the next line switch to
  \dospecials        %    verbatim-category-code-régime.
  \catcode`\{=1      % <- give opening curly brace the usual catcode so a 
                     %    curly-brace-balanced argument can be gathered in
                     %    case of the first thing of the verbatimized-argument 
                     %    being a curly opening brace.
  \catcode`\ =10     % <- give space and horizontal tab the usual catcode so \UD@collectverbarg
  \catcode`\^^I=10   %    cannot catch a space or a horizontal tab as its 4th undelimited argument.
                     %    (Its 4th undelimited argument denotes the verbatim-
                     %     syntax-delimiter in case of not gathering a
                     %     curly-brace-nested argument.)
  \kernel@ifnextchar\bgroup
  {% seems a curly-brace-nested argument is to be caught:
    \catcode`\}=2    % <- give closing curly brace the usual catcode also.
    \UD@collectverbarg{#1}{#2}{#3}{}%
  }{% seems an argument with verbatim-syntax-delimiter is to be caught:
    \do\{% <- give opening curly brace the verbatim-catcode again.
    \UD@collectverbarg{#1}{#2}{#3}%
  }%
}%
\newcommand\UD@collectverbarg[4]{%
  \do\ %   <- Now that \UD@collectverbarg has the delimiter or
  \do\^^I%    emptiness in its 4th arg, give space and horizontal tab
         %    the verbatim-catcode again.
  \do\^^M% <- Give the carriage-return-character the verbatim-catcode.
  \long\def\@tempb##1#4{%
    %\edef\@tempb{##1}%
    \def\@tempb{##1}%
    \@onelevel@sanitize\@tempb % <- Turn characters into their "12/other"-pendants.
                               %    This may be important with things like the 
                               %    inputenc-package which may make characters 
                               %    active/which give them catcode 13(active).
    \expandafter\UDEndlreplace\expandafter{\@tempb}{#1}{\def\@tempb}% <- this starts 
                               %    the loop for replacing endline-characters.
    \expandafter\UD@@collectverbarg\expandafter{\@tempb}{#2}{#3}% <- this "spits 
                               %    out the result.
  }%
  \@tempb
}%
\newcommand\UD@@collectverbarg[3]{%
  \endgroup
  #2{#3{#1}}%
}%
%%<---------------- End of code for \UDcollectverbarg ----------------->

\usepackage{listings}

% As a usage-example let's now define a macro \ifflag which
% collects two verbatim-arguments and does spit out one of them wrapped into
% \scantokens.
% Basically \ifflag is a wrapper for calling \UDcollectverbarg and
% passing the verbatimized arguments to \@ifflag
\newcommand\ifflag{%
  \UDcollectverbarg{^^J}%
                   {\UDcollectverbarg{^^J}{\@firstofone}}%
                   {\@ifflag}%
}%
\newcommand\@ifflag[2]{%
  \ifdefined\flag\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {\scantokens{#1}}%
  {\scantokens{#2}}%
}%
\makeatother

\begin{document}

Listing when flag not defined:

\ifflag{
\begin{lstlisting}
foo1
   foo2
foo3
\end{lstlisting}
}{
\begin{lstlisting}
bar1
   bar2
bar3
\end{lstlisting}
}

\def\flag{}

Listing when flag defined:

\ifflag{
\begin{lstlisting}
foo1
   foo2
foo3
\end{lstlisting}
}{
\begin{lstlisting}
bar1
   bar2
bar3
\end{lstlisting}
}

\end{document}

在此处输入图片描述

答案2

这是大师 @Ulrich Diez 给出的解决方案的替代解决方案。该包scontents允许存储verbatim要重复使用的内容,我只对您的示例条目做了一些修改:

\documentclass{article}
\usepackage{listings}
\usepackage{scontents}
\def\flag{}
\newcommand{\ifflag}[2]{%
\ifdefined\flag #1
\fi
\unless\ifdefined\flag #2
\fi}
\begin{document}
% foo
\begin{scontents}[store-env=foo]
\begin{lstlisting}
foo
\end{lstlisting}
\end{scontents}
% baz
\begin{scontents}[store-env=baz]
\begin{lstlisting}
foo
\end{lstlisting}
\end{scontents}

\ifflag{\getstored[1]{foo}}{\getstored[1]{baz}}

\end{document}

问候语

相关内容