如何简化(长)命令的编写

如何简化(长)命令的编写

我问自己如何才能轻松地编写以完全相同的方式经常使用的长命令/短语。

例如:我怎样才能编写以下内容:

\begin{mdframed}\begin{lstlisting}[style=mystyle]
bla bla bla
\end{lstlisting}\end{mdframed}

简而言之:

\mycommand{
bla bla bla
}

编辑: 根据要求,我将在下面添加对我的原始问题的更新:

这是缺失的样式定义:

\lstdefinestyle{mystyle}{
    language = C,
    basicstyle=\footnotesize\ttfamily,
    keywordstyle = {\color{DarkGreen}\bfseries},
    morekeywords = {Send,Receive},
    mathescape=true,
    columns=fullflexible,
    prebreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\rhookswarrow}},
    breakindent=0
}

因此,正如您所看到的,我还希望在里面有 mathescape 选项,以便执行如下操作:

\mycommand{
bla bla bla $1+2=3$
}

哪一个应该不是结果转化为文本"$1+2=3$",但转化1+2=3为数学解释。

答案1

我不建议将mdframedlstlisting变成带有参数的命令,而不是适当的环境。

首先,换行符不能保留在参数版本中。

我还建议使用tcolorbox而不是mdframed,因为它可以更好地与 集成listings。无论如何,你可以这样做

\documentclass{article}
\usepackage{listings,mdframed}

\lstnewenvironment{mylisting}
   {\mdframed\lstset{}}
   {\endmdframed}

\begin{document}

\begin{mylisting}
bla bla bla
\end{mylisting}

\end{document}

请注意辅助命令\mdframed\endmdframed,否则\lstnewenvironment声明会产生根本不起作用的东西。我删除了,style=mystyle因为我不知道样式是如何定义的。在的参数中添加您需要的所有必要选项\lstset

具有风格的扩展版本


\documentclass{article}
\usepackage{listings,mdframed}
\usepackage[svgnames]{xcolor}

\lstdefinestyle{tmstyle}{
  language = C,
  basicstyle=\footnotesize\ttfamily,
  keywordstyle = {\color{DarkGreen}\bfseries},
  morekeywords = {Send,Receive},
  mathescape=true,
  columns=fullflexible,
  prebreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\rhookswarrow}},
  breakindent=0,
}

\lstnewenvironment{mylisting}
   {\mdframed\lstset{style=tmstyle}}
   {\endmdframed}

\begin{document}

\begin{mylisting}
Send bla bla bla $1+2=3$
\end{mylisting}

\end{document}

在此处输入图片描述

答案2

随着即将于 10 月发布的 LaTeX 内核的推出,您可以添加“钩子”来实现这一点。例如

\documentclass{article}
\usepackage{listings}
\usepackage{mdframed}
\AddToHook{env/lstlisting/before}{\begin{mdframed}}
\AddToHook{env/lstlisting/after}{\end{mdframed}}
\begin{document}
\begin{lstlisting}
bla bla bla
\end{lstlisting}
\end{document}

(您需要运行例如pdflatex-dev现在才能使其工作:它使用内核的预发布版本。)

答案3

一般来说,你可以做这样的事情:

\newcommand{\mycommand}[1]{
  \begin{environment}
   #1
  \end{environment}
}

但是,lstlisting环境依赖于改变类别代码来工作,因此当参数扩展时,类别代码将不会是它们所需要的。

幸运的是,这个问题是可以预见的。相反,您需要使用命令,\lstnewenvironment以便可以执行如下操作:

\lstnewenvironment{mylisting}
   {\begin{mdframed}\lstset{style=mystyle}}
   {\end{mdframed}}

然后使用它

\begin{mylisting}
bla bla bla
\end{mylisting}

答案4

只要\mycommand仅在文档级别使用,而不是来自其他宏的参数中,不是来自其他宏的定义文本中,不是来自标记寄存器中,不是作为移动参数的一部分,您就可以让 LaTeX 将内容读取为逐字参数并将它们传递给\scantokens

但是我不推荐这样做,因为这样缩进要显示的代码肯定不会更容易:

\documentclass{article}
\usepackage{xparse}
\usepackage{listings}
\usepackage{mdframed}

\lstdefinestyle{mystyle}{%
    language = C,
    basicstyle=\footnotesize\ttfamily,
    keywordstyle = {\color{DarkGreen}\bfseries},
    morekeywords = {Send,Receive},
    mathescape=true,
    columns=fullflexible,
    prebreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\rhookswarrow}},
    breakindent=0
}


\newcommand\mycommand{%
  \begingroup
  \catcode`\^^I=12 %
  \newlinechar=\endlinechar
  \MyReadVerbatimArgument
}%
\begingroup
\NewDocumentCommand\MyReadVerbatimArgument{+v +v}{%
  \endgroup
  \NewDocumentCommand\MyReadVerbatimArgument{+v}{%
    \scantokens{\endgroup#1##1#2}%
  }%
}%
\MyReadVerbatimArgument|\begin{mdframed}
\begin{lstlisting}[style=mystyle]
||
\end{lstlisting}
\end{mdframed}
%|

\begin{document}

\mycommand{bla bla bla
           bla bla bla
bla bla bla}

\end{document}

在此处输入图片描述



如果您愿意,可以扩展此功能以从整个逐字参数中删除前导结束符(如果存在)以及从整个逐字参数中删除尾随结束符(如果存在)。

这边走

\mycommand{
           bla bla bla
           bla bla bla
This line is not indented.
           bla bla bla
           bla bla bla
}

\mycommand{           bla bla bla
           bla bla bla
This line is not indented.
           bla bla bla
           bla bla bla
}

\mycommand{
           bla bla bla
           bla bla bla
This line is not indented.
           bla bla bla
           bla bla bla}

\mycommand{           bla bla bla
           bla bla bla
This line is not indented.
           bla bla bla
           bla bla bla}

都会产生相同的输出。

删除前导/尾随行尾字符(即^^M类别代码 12(其他)的前导/尾随字符 = 类别代码 12(其他)的前导/尾随返回字符)的技巧与从宏参数中删除前导/尾随空格标记的技巧相同。
使用辅助宏时,不要使用空格标记作为参数分隔符,而是使用类别代码 12(其他)的显式返回字符标记。

后者(删除前导/尾随空格)在 Michael Downes 的“Around the Bend”TeX 挑战集的第 15 个练习中进行了讨论。

可以在以下位置找到由 Peter Wilson 编写的 pdf 文件,其中包含“Around the Bend”系列 TeX 挑战的所有挑战 / 练习和所有解决方案 / 答案:
http://mirrors.ctan.org/info/challenges/AroBend/AroundTheBend.pdf

挑战/练习的原文可以在以下位置找到:
http://mirrors.ctan.org/info/challenges/aro-bend/exercise.015

提交的解决方案/答案的讨论原文可以在以下位置找到:
http://mirrors.ctan.org/info/challenges/aro-bend/answer.015

\documentclass{article}
\usepackage{xparse}
\usepackage{listings}
\usepackage{mdframed}


%%/////////////////////////////////////////////////////////////////////////////
%% Begin of code for  \UDRemoveLeadingNTrailingEndl
%%/////////////////////////////////////////////////////////////////////////////
\makeatletter
%%=============================================================================
%% 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\UDExchange[2]{#2#1}%
\begingroup
% Dummy-definition, will be overridden. Is used only to get ^^M of 
% category code 12(other) as #1 into subsequent definition-texts:
\NewDocumentCommand\UD@CheckWhetherLeadingEndl{+m}{%
  \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}%
    }%
  }%
  %%===========================================================================
  %% Remove one leading endline-character from _verbatimized_ argument
  %%===========================================================================
  \@ifdefinable\UD@@TrimLeadingEndl{\long\def\UD@@TrimLeadingEndl#1{}}%
  %%===========================================================================
  %% 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}%
    }%
  }%
  %%===========================================================================
  %% Remove one trailing endline-character from _verbatimized_ argument
  %%===========================================================================
  \newcommand\UD@TrimTrailingEndl[1]{\UD@@TrimTrailingEndl##1\UD@SelDom}%
  \@ifdefinable\UD@@TrimTrailingEndl{%
    \long\def\UD@@TrimTrailingEndl##1#1\UD@SelDom{##1}%
  }%
  %%===========================================================================
  %% Remove one leading and one trailing endline-character from _verbatimized_ 
  %% argument if present. Due to \romannumeral0-expansion the result is
  %% delivered in 2 expansion-steps:
  %%===========================================================================
  \newcommand\UDRemoveLeadingNTrailingEndl[1]{%
    \romannumeral0%
    \UD@CheckWhetherLeadingEndl{##1}{%
      \UD@CheckWhetherTrailingEndl{##1}{%
         \UDExchange{ }{%
           \expandafter\expandafter\expandafter\expandafter
           \expandafter\expandafter\expandafter
         }%
         \expandafter\UD@TrimTrailingEndl\expandafter{\UD@@TrimLeadingEndl##1}%
       }{%
         \UDExchange{ }{\expandafter}%
         \UD@@TrimLeadingEndl##1%
       }%
    }{%
      \UD@CheckWhetherTrailingEndl{##1}{%
         \UDExchange{ }{\expandafter\expandafter\expandafter}%
         \UD@TrimTrailingEndl{##1}%
      }{ ##1}%
    }%
  }%
}%
%%-----------------------------------------------------------------------------
%% 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,
%% close the group, override itself and define all those macros where that
%% catcode-12-^^M  is needed:
\catcode`\^^M=12 %
\UD@CheckWhetherLeadingEndl{^^M}%
\makeatother
%%/////////////////////////////////////////////////////////////////////////////
%% End of code for  \UDRemoveLeadingNTrailingEndl
%%/////////////////////////////////////////////////////////////////////////////


\lstdefinestyle{mystyle}{
    language = C,
    basicstyle=\footnotesize\ttfamily,
    keywordstyle = {\color{DarkGreen}\bfseries},
    morekeywords = {Send,Receive},
    mathescape=true,
    columns=fullflexible,
    prebreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\rhookswarrow}},
    breakindent=0
}

\newcommand\mycommand{%
  \begingroup
  \catcode`\^^I=12 %
  \newlinechar=\endlinechar
  \MyReadVerbatimArgument
}%
\begingroup
\NewDocumentCommand\MyReadVerbatimArgument{+v +v}{%
  \endgroup
  \NewDocumentCommand\MyReadVerbatimArgument{+v}{%
    \scantokens\expandafter{\romannumeral0%
     \expandafter\expandafter\expandafter\UDExchange
     \expandafter\expandafter\expandafter{%
       \UDRemoveLeadingNTrailingEndl{##1}%
     }{ \endgroup#1}#2}%
  }%
}%
\MyReadVerbatimArgument|\begin{mdframed}
\begin{lstlisting}[style=mystyle]
||
\end{lstlisting}
\end{mdframed}
%|

\begin{document}

\mycommand{
           bla bla bla
           bla bla bla
This line is not indented.
           bla bla bla
           bla bla bla
}


\mycommand{           bla bla bla
           bla bla bla
This line is not indented.
           bla bla bla
           bla bla bla
}


\mycommand{
           bla bla bla
           bla bla bla
This line is not indented.
           bla bla bla
           bla bla bla}


\mycommand{           bla bla bla
           bla bla bla
This line is not indented.
           bla bla bla
           bla bla bla}

\end{document}

在此处输入图片描述

关于 TeX 工作方式的细微差别的一些评论:

在任何情况下,TeX 读取和标记输入的机制都会丢弃 .tex 输入行末尾的所有空格字符,然后添加一个字符,该字符在 TeX 引擎的内部字符编码方案中的编码点号(对于传统引擎,为 ASCII;对于基于 XeTeX/LuaTeX 的引擎,为 UTF-8/Unicode;ASCII 可以被视为 Unicode 的严格子集)等于整数参数的值,\endlinechar然后才开始标记该行。通常,\endlinechar-parameter 的值为 13,表示返回字符,在 TeX 的^^-notation 中也可以访问为。(M 是拉丁字母中的^^M13 个字母。)

这意味着包含空格字符(ASCII 32(dec)/Unicode 32(dec))的 .tex 输入行在任何情况下都将被视为空行。

这也意味着,如果 .tex 输入行以空格字符结尾(ASCII 32(dec)/Unicode 32(dec)),这些空格字符将被丢弃。
您可以通过将以空格字符序列结尾的文本行放入环境来测试这一点verbatim*

相关内容