NewDocumentCommand 中的单行或多行逐字参数

NewDocumentCommand 中的单行或多行逐字参数

我想创建一个命令来显示给定的LaTeX代码及其格式化的输出。到目前为止,我注意到包做得很好,因为您可以使用包中showexpl通过命令定义的样式以及一些其他原因。\lstdefinestyle{}listings

问题

现在,借鉴以下问题的公认答案

我已经创建了以下命令

\usepackage{showexpl}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\LTXIO}{+v}{
  \exp_args:Nx \scantokens
  {
        \string\LTXexample[\unexpanded{pos=b, numbers=none, basicstyle=\ttfamily}]
            #1
    \string\endLTXexample
  }
}
\ExplSyntaxOff

用法如下

\LTXIO
{
    \Huge{another}
    \\\scriptsize{another}
}

在此处输入图片描述

但是,当要测试的命令写成一行时(例如\LTXIO{\Huge{another}}),创建的命令会出现编译错误。显示的编译错误是

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

所以问题是如何使LTXIO命令处理单行和多行?

答案1

问题有三个方面:

第一点:

\scantokens假装将未扩展的内容写入外部文本文件,然后输入该外部文本文件,从而该外部文本文件的内容在当前类别代码制度下被标记化。

+v-arguments 将行尾编码为 -类别代码 12(其他)的字符 = 类别代码 12(其他)的回车符,而在执行整数参数的值^^M时仍表示字符= 换行符。\scantokens\newlinechar^^J

您需要确保在将未扩展的内容伪造到外部文件时\newlinechar,的值表示^^M-character=回车符。\scantokens

方面2:

如果代码只有一行,则调用\begin{LTXexample}[...]和第一行代码之间必须有换行符。
最后一行代码和 之间也必须有换行符\end{LTXexample}

因此,您需要一个例程来检测代码序列是否被换行符包围。

这可以以与检查前导和尾随空格相同的方式实现。关于空格标记,它与^^M类别代码 12(其他)的字符标记 = 类别代码 12(其他)的回车字符标记有关,仅此而已。

检测前导空格和尾随空格的讨论见Michael Downes 的绕弯子答案 15。 (练习15是关于编写宏来修剪宏参数中的前导和尾随空格。

彼得·威尔逊 (Peter Wilson) 创建了一个格式良好的 pdf 文件,其中包含所有 Around-the-Bend 练习和答案。

方面三:

行缩进的处理。

的参数\LTXIO可以包含多行代码。

这样的代码缩进该如何处理?

在下面的例子中,这种缩进保持原样。

\documentclass{article}
\usepackage{xparse}
\usepackage{showexpl}

\makeatletter
%%=============================================================================
%% Work around showexpl's weird overriding of xleftmargim/xrightmargin in case
%% of having no codeline-numbers -- that weirdness is in \SX@codeInput.
%%=============================================================================
\lst@Key{nocodelinenumbers}{false}[true]{%
    \lstKV@SwitchCases{#1}{%
       false:\\%
       true:\long\def\lst@PlaceNumber{}%
     }{\PackageError{Listings}{nocodelinenumbers #1 unknown}\@ehc}%
}%
%%=============================================================================
%% 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>}%
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\if\relax\detokenize{#1}\relax
  \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {\@firstoftwo{\expandafter}{} \@firstoftwo}%
  {\@firstoftwo{\expandafter}{} \@secondoftwo}%
}%
%%=============================================================================
%% Exchange arguments
%%=============================================================================
\newcommand\UD@Exchange[2]{#2#1}%
\begingroup
% Dummy-definition, will be overridden. Is used only to get ^^M of 
% category code 12(other) as #1 and verbatimized calls as #2 and #3
% into subsequent definition-texts:
\NewDocumentCommand\UD@CheckWhetherLeadingEndl{+m+v+v}{%
  \endgroup
  %%===========================================================================
  %% Check whether_verbatimized_ argument starts with a endline-character
  %%===========================================================================
  %% \UD@CheckWhetherLeadingEndl{<Argument which is to be checked>}%
  %%                            {<Tokens to be delivered in case <argument
  %%                              which is to be checked>'s 1st token is a
  %%                              endline-charactern>}%
  %%                            {<Tokens to be delivered in case <argument
  %%                              which is to be checked>'s 1st token is not
  %%                              a endline-character>}%
  \newcommand\UD@CheckWhetherLeadingEndl[1]{%
    \UD@@CheckWhetherLeadingEndl\UD@SelDom##1\UD@SelDom#1\UD@@SelDom
  }%
  \@ifdefinable\UD@@CheckWhetherLeadingEndl{%
    \long\def\UD@@CheckWhetherLeadingEndl##1\UD@SelDom#1##2\UD@@SelDom{%
      \UD@CheckWhetherNull{##2}{\@secondoftwo}{\@firstoftwo}%
    }%
  }%
  %%===========================================================================
  %% Check whether _verbatimized_ argument ends with a endline-character
  %%===========================================================================
  %% \UD@CheckWhetherTrailingEndl{<Argument which is to be checked>}%
  %%                             {<Tokens to be delivered in case <argument
  %%                               which is to be checked>'s last token is a
  %%                               endline-charactern>}%
  %%                             {<Tokens to be delivered in case <argument
  %%                               which is to be checked>'s last token is not
  %%                               a endline-character>}%
  \newcommand\UD@CheckWhetherTrailingEndl[1]{%
    \UD@@CheckWhetherTrailingEndl##1\UD@SelDom#1\UD@SelDom\UD@@SelDom
  }%
  \@ifdefinable\UD@@CheckWhetherTrailingEndl{%
    \long\def\UD@@CheckWhetherTrailingEndl##1#1\UD@SelDom##2\UD@@SelDom{%
      \UD@CheckWhetherNull{##2}{\@secondoftwo}{\@firstoftwo}%
    }%
  }%
  %%===========================================================================
  %%  \LTXIO
  %%===========================================================================
  \newcommand*\LTXIO{%
    \begingroup
    % Make sure endline-chars yield linebreaks when \scantokens
    % does its fake-writing-part:
    \newlinechar=`\^^M %
    % Make sure horizontal tabs won't cause problems when LTXIO reads its
    % +v-argument:
    \@makeother\^^I%
    \InnerLTXIO
  }%
  \NewDocumentCommand{\InnerLTXIO}{+v}{%
    \scantokens\expandafter{%
        \romannumeral0%
        \UD@CheckWhetherLeadingEndl{##1}{%
          \UD@CheckWhetherTrailingEndl{##1}{%
            \UD@Exchange{##1}%
          }{%
            \UD@Exchange{##1#1}%
          }%
        }{%
          \UD@CheckWhetherTrailingEndl{##1}{%
            \UD@Exchange{#1##1}%
          }{%
            \UD@Exchange{#1##1#1}%
          }%
        }%
        { \endgroup#2}%
        #3%
    }%
  }%
}%
%%-----------------------------------------------------------------------------
%% Now let's change the catcode of ^^M and then call the dummy-definition
%% of \UD@CheckWhetherLeadingEndl so that it can fetch the catcode-12-^^M
%% and the verbatim-phrases for closing the group and overriding itself
%% and defining all those macros where that catcode-12-^^M and those
%% verbatimized phrases are needed:
\catcode`\^^M=12 %
\UD@CheckWhetherLeadingEndl%
  {^^M}%
  {\begin{LTXexample}[pos=b, frame=single, linewidth=\textwidth, xleftmargin=3.4pt, 
   xrightmargin=3.4pt, basicstyle=\ttfamily, nocodelinenumbers, showspaces=true,
   showtabs=true]}%
  {\end{LTXexample}}%
\makeatother

\begin{document}

\noindent\textbf{The input}

\begin{verbatim*}
\LTXIO
{
  {%
    \Huge
    another
    piece
    of
    text%
  }\\
  {\scriptsize another piece of text}
}
\end{verbatim*}

\noindent\textbf{yields:}\vspace*{\partopsep}\vspace*{\topsep}

\LTXIO
{
  {%
    \Huge
    another
    piece
    of
    text%
  }\\
  {\scriptsize another piece of text}
}

\bigskip

\noindent\null\hrule height 2pt\hfill\null

\bigskip

\noindent\textbf{The input}

\begin{verbatim*}
\LTXIO{{\Huge another piece of text}}
\end{verbatim*}

\noindent\textbf{yields:}\vspace*{\partopsep}\vspace*{\topsep}

\LTXIO{{\Huge another piece of text}}

\end{document}

在此处输入图片描述

相关内容