如果逐字文本中的制表符,为什么带有 lstlisting 的宏的解决方案会失败?

如果逐字文本中的制表符,为什么带有 lstlisting 的宏的解决方案会失败?

例如此解决方案对于带有lstlistinginside 的宏,效果很好,除非逐字文本包含制表符(如下面的 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\scantokenslstlisting\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}

在此处输入图片描述

相关内容