例如此解决方案对于带有lstlisting
inside 的宏,效果很好,除非逐字文本包含制表符(如下面的 MCE 所示)。为什么?
\documentclass{article}
\usepackage{listings}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\code}{m +v}{
\exp_args:Nx \scantokens
{
\string\begin{lstlisting}[\unexpanded{language=#1,numbers=none,xleftmargin=0.35cm,framesep=0mm}]
#2
\string\end{lstlisting}
}
}
\ExplSyntaxOff
\begin{document}
\section{Works well}
\code{C}{
Foo bar
}
\section{Fails}
\code{C}{
Foo bar
}
\end{document}
答案1
\dospecials
,它设置了逐字环境的特殊 catcode,不包括制表符 ( ^^I
)。TeX 随后将制表符视为常规空格,当看到这种情况时,逐字扫描器会发出警告。您可以添加\do\^^I
以\dospecials
使其正常工作(在下面的代码中,我将其添加到组中,因此它不会对您的命令之外产生任何影响):
\documentclass{article}
\usepackage{listings}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \code { }
{
\group_begin:
\tl_put_right:Nn \dospecials { \do \^^I }
\code_aux:nw
}
\NewDocumentCommand \code_aux:nw { m +v }
{
\exp_args:Nx \scantokens
{
\string\begin{lstlisting}[\unexpanded
{language=#1,numbers=none,xleftmargin=0.35cm,framesep=0mm}]
#2
\string\end{lstlisting}
}
\group_end:
}
\ExplSyntaxOff
\begin{document}
\section{Works well}
\code{C}{
Foo bar
}
\section{Fails (a tabulation is supposed to be between "Foo" and "bar")}
\code{C}{
Foo bar
}
\end{document}
答案2
除了 +v 参数预处理不考虑水平制表符(^^I
在 TeX 的^^
符号中)的字符代码的情况之外,即使带有 v/+v 类型参数的水平制表符通常也会被标记为类别 10(空格)和字符代码 32 的显式空格标记,而 v/+v 参数故意被实现为在包含显式空格标记的情况下触发错误消息,我建议不要使用, \exp_args:Nx
因为这可能会导致在使用 inputenc 时不必要的活动字符扩展。相反,您可以使用一个临时宏,它读取短语\begin{lstlistig}
并\end{lstlisting}
在修改后的 catcode 制度下,然后定义非临时变量。
并且您可能希望通过在写入假文件时\newline
暂时给出\newlinechar
的值来中和字符机制,以确保即使有人使用参数,环境和命令也能以相同的方式运行。\endlinechar
\scantokens
lstlisting
\code
\newlinechar
\documentclass{article}
\usepackage{listings}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\code}{m}{
\group_begin:
\char_set_catcode_other:N \^^I
\innercode{#1}
}
\group_begin:
\NewDocumentCommand{\innercode}{mmm}{
\group_end:
\NewDocumentCommand{\innercode}{m +v}{
\group_end:
\group_begin:
% See the subtle difference between \code and the lstlisting-environment
% when turning the following line into a comment:
\tex_newlinechar:D=\tex_endlinechar:D
%
\tex_scantokens:D
{
#3
#1[language=##1,numbers=none,xleftmargin=0.35cm,framesep=0mm]%
##2
#2%<-here \scantokens attaches an endline-chracter=return
}
}
}
\use:n{
\char_set_catcode_other:N \\
\char_set_catcode_other:N \{
\char_set_catcode_other:N \}
\char_set_catcode_group_begin:N \[
\char_set_catcode_group_end:N \]
\innercode
}[\begin{lstlisting}][\end{lstlisting}][\endgroup~]
\ExplSyntaxOff
\begin{document}
\section{Works well}
\code{C}{
Foo bar
}
\section{A tabulation is supposed to be between "Foo" and "bar"}
\newlinechar=`\A
\code{C}{
Foo bar
Foo bar
Foo bar
Foo barAFoo bar
}
\begin{lstlisting}[language=C,numbers=none,xleftmargin=0.35cm,framesep=0mm]
Foo bar
Foo bar
Foo bar
Foo barAFoo bar
\end{lstlisting}
\end{document}