我想知道是否有一种方法可以在lstlisting
环境内扩展宏,并自动将语法突出显示应用于扩展的文本。
例如,
\documentclass{article}
\usepackage{listings}
\usepackage{xcolor}
\lstset{
basicstyle=\ttfamily,
keywordstyle=\color{blue},
keywords={function},
escapeinside={|}{|}
}
\def\fbar{function bar ();}
\begin{document}
\begin{lstlisting}[]
function foo ();
|\fbar|
\end{lstlisting}
\end{document}
应该产生这样的输出:
答案1
现在有啦! 我现在也讨厌列表:)
嗯,有点儿像。继续阅读你就会明白。
首先,escapechar=<char>
令我们失望的是,该选项只是 的简写escapeinside=<char><char>
。在我看来,它没什么用。文档和资料没有指出除此以外的任何内容。
我实现了一个cschar=<char>
选项,它将使用<char>
作为转义符(在 TeX 中,而不是listings
' 的意思)。我从listings
'中回收了一些代码escapeinside
并让它开始一个\lst@scan@csname
事情。这个扫描器尽可能多地抓取a-z
和A-Z
(\m@cr@s
不允许)之间的字符并从中构建一个控制序列,或者它抓取一个非字母字符并构建一个控制字符。然后扩展构建的控制序列/字符(一次)并使用expl3
's重新标记\tl_rescan:nn
(我没有耐心复制它的定义)并重新插入到标记流中以listings
执行其操作。如果扩展的控制序列恰好有另一个控制序列,则它稍后也会扩展,并以递归方式工作。
请注意,这是一个仿真TeX 构建控制序列的方式,因此它有一些不值得检查的错误\fbar hello
功能。例如,将打印function bar(); hello
,中间有一个空格,而 TeX 会扩展为function bar();hello
。这使得您无法获得,例如function bar();hello
因为\fbarhello
将被解释为一个(未定义的)控制序列。您需要定义一个控制序列\def\hello{hello}
并使用\fbar\hello
。
代码如下:
\documentclass{article}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{expl3}
\makeatletter
\gdef\lst@CSEscape#1{%
\lst@CArgX #1\relax\lst@CDefX
{}%
{\lst@ifdropinput\else
\lst@TrackNewLines\lst@OutputLostSpace \lst@XPrintToken
\lst@InterruptModes
\lst@EnterMode{\lst@TeXmode}{\lst@modetrue}%
\let\do\@makeother\dospecials
\let\lst@grabbed@csname\@empty
\expandafter\lst@scan@csname%
\fi}%
{}}
\lst@Key{cschar}{}
{\ifx\@empty#1\@empty
\let\lst@CSChar\relax
\else
\def\lst@CSChar{\lst@CSEscape{#1}}%
\fi}
\let\lst@explicit@space=\ %
\lst@AddToHook{SelectCharTable}{\lst@CSChar}
\def\lst@afterfi#1#2\fi{\fi#1}
\def\lst@scan@csname#1{%
\if\relax\detokenize{#1}\relax
\lst@afterfi{\lst@dispatch@csname{#1}}%
\else
\lst@afterfi{%
\uppercase{\def\lst@tmpa{#1}}%
\expandafter\lst@scan@csname@\expandafter
{\number\expandafter`\lst@tmpa}{#1}}%
\fi}
\def\lst@scan@csname@#1#2{%
\ifnum % vv If you want to \maketatletter :)
% \ifnum0#1>63 \expandafter0\else\expandafter1\fi
\ifnum0#1>64 \expandafter0\else\expandafter1\fi
\ifnum0#1<91 \expandafter0\else\expandafter1\fi
=0
\edef\lst@grabbed@csname{\lst@grabbed@csname\string#2}%
\expandafter\lst@scan@csname
\else
\lst@afterfi{\lst@dispatch@csname{#2}}%
\fi}
\def\lst@dispatch@csname#1{%
\ifx\lst@grabbed@csname\@empty
\if\detokenize{#1}\lst@char@space
\lst@afterfi{\lst@afterfi{\lst@return@from@csname@{}{#1}}}%
\else
\lst@afterfi{\lst@afterfi{\lst@dispatch@csname@{\string#1}}}%
\fi
\else
\lst@afterfi{\expandafter\lst@dispatch@csname@\expandafter{\lst@grabbed@csname}{#1}}%
\fi}
\def\lst@dispatch@csname@#1{%
\@ifundefined{#1}%
{\PackageError{Listings}{! Undefined control sequence \@backslashchar#1}}%
{\expandafter\lst@return@from@csname\csname#1\endcsname}}
\def\lst@return@from@csname#1{%
\ifx#1\lst@explicit@space
\lst@afterfi{\lst@return@from@csname@{}{}}%
\else
\lst@afterfi{\expandafter\lst@return@from@csname@\expandafter{#1}}%
\fi}
\def\lst@return@from@csname@#1#2{%
\lst@escapeend \lst@LeaveAllModes\lst@ReenterModes
\lst@newlines\z@ \lst@whitespacefalse
\lst@csescape@output{#1}{#2}}
\def\if@single@item#1{%
\if\relax\detokenize\expandafter{\@gobble#1}\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondofone
\fi}
\def\lst@active@space{\lst@ProcessSpace}
\begingroup
\catcode`\|=0
\catcode`\[=1
\catcode`\]=2
\let\do\@makeother\dospecials
[|global|let|lst@char@space= ]
|endgroup
\def\lst@csescape@output#1#2{%
\if@single@item{#2}%
{%
\ifnum
\ifx\lst@active@space#2\expandafter1\else\expandafter0\fi
\ifx\lst@char@space#2\expandafter1\else\expandafter0\fi
>0
\def\lst@aftertokens{\safescantokens{#1}\lst@ProcessSpace}%
\else
\def\lst@aftertokens{\safescantokens{#1#2}}%
\fi
}{%
\def\lst@aftertokens{\safescantokens{#1#2}}%
}%
\lst@aftertokens
}
\makeatother
\ExplSyntaxOn
\cs_new_protected:Npn \safescantokens #1 { \tl_rescan:nn {} {#1}}
\ExplSyntaxOff
\begin{document}
\lstset{
basicstyle=\ttfamily,
keywordstyle=\color{blue},
keywords={function},
cschar=\\,
}
\def\fbar{function bar ();}
\def\fuba{cat fuba ();}
\def\1{function first ();}
\begin{lstlisting}[]
function foo ();@12\ 3
\fbar\1{}@12\ 3
\fbar\fuba@12\ 3
\end{lstlisting}
\begin{lstlisting}[]
function foo (); @12\ 3
\fbar{} @12\ 3
\fbar @12\ 3
\fbar\ @12\ 3
\end{lstlisting}
\lstset{cschar=|}
\begin{lstlisting}[]
function foo ();@12\ 3
|fbar{}@12\ 3
|fbar@12\ 3
\end{lstlisting}
\begin{lstlisting}[]
function foo (); @12\ 3
|fbar{} @12\ 3
|fbar @12\ 3
|fbar\ @12\ 3
\end{lstlisting}
\end{document}
输出:
我会小心使用它。这种代码让人不太放心。
PS:如果您想让代码将更多字符识别为“字母”,则必须更改条件:
\ifnum % vv If you want to \maketatletter :)
% \ifnum0#1>63 \expandafter0\else\expandafter1\fi
\ifnum0#1>64 \expandafter0\else\expandafter1\fi
\ifnum0#1<91 \expandafter0\else\expandafter1\fi
=0
例如,将第一个改为64
将63
使其@
成为字母。
答案2
\documentclass{article}
\usepackage{listings}
\usepackage{xcolor}
\lstset{
basicstyle=\ttfamily,
keywordstyle=\color{blue},
keywords={function},
escapeinside={|}{|}
}
\def\fbar{function bar ();}
\begin{document}
\begin{lstlisting}[]
function foo ();
|\bgroup\endlinechar=-1 \scantokens\expandafter{\expandafter\egroup\expandafter|\fbar}
function baz ();
\end{lstlisting}
\end{document}
\documentclass{article}
\usepackage{listings}
\usepackage{xcolor}
\lstset{
basicstyle=\ttfamily,
keywordstyle=\color{blue},
keywords={function},
escapeinside={|}{|}
}
\begingroup
\catcode`\^^M=12 %
\csname@firstofone\endcsname{%
\endgroup%
\def\fbar{%
function bar ();
function baz ();%
}%
}%
%
\begin{document}
\begin{lstlisting}[]
function foo ();
|\bgroup\endlinechar=-1 \scantokens\expandafter{\expandafter\egroup\expandafter|\fbar} Something in the same line as baz;
function bat ();
\end{lstlisting}
\end{document}