2

I tried to use the escapechar option with lstinline. In order to "activate it", I used the approach described here. However, when using it within a macro, it breaks. This is shown below. Is there a way to fix this?

Illustration of the error

\documentclass{article}

%for lstinline
\usepackage{listings}

%for highlighting
\usepackage{xcolor}

%to allow escapechar inside lstinline
\usepackage{etoolbox}
\makeatletter
\patchcmd{\lsthk@TextStyle}{\let\lst@DefEsc\@empty}{}{}{\errmessage{failed to patch}}
\makeatother

%the example macro
\def\mymacro#1{\lstinline[escapechar=§]|§#1§|}

%for demonstration purposes
\def\perfectly{perfectly}

%highlight lstinline
\lstset{
    basicstyle=\ttfamily\color{blue}
}

\begin{document}
    \lstinline[escapechar=§]|works §\perfectly§| - listinline no more active

    \mymacro{doesn't work} - lstinline is still active here
\end{document}
plauer
  • 438
  • Verbatim macros like \lstinline are problematic when they are used in arguments of other commands and even when you define new macros involving them. This has to do with how TeX reads the arguments of macros. \lstinline can't 'sanitize' its argument as usual when it is called from within other macros (because essentially the argument can't be changed any more): See https://tex.stackexchange.com/q/17153/35864, https://texfaq.org/FAQ-verbwithin – moewe May 10 '20 at 04:56
  • \newcommand\mymacro{\lstinline[escapechar=§]} would not throw an error, but doesn't automatically include the escapechar, it would have to be used as \mymacro|§\perfectly§|, which probably isn't the improvement you are looking for. Given that the \mymacro from your code would just land you in an escaped listings where special characters again have their usual meaning, it could be replaced by \newcommand\mymacro[1]{{\ttfamily\color{blue} #1}}. – moewe May 10 '20 at 04:59
  • Unrelated to the problem, but in general it is preferable to use \newcommand instead of \def to avoid accidental overwriting of existing definitions. – moewe May 10 '20 at 04:59
  • @moewe Thanks for the post. I didn't find that. \newcommand\mymacro{\lstinline[escapechar=§]} won't work for my actual application since there text before and after the §#1§ and the same goes for \newcommand\mymacro[1]{{\ttfamily\color{blue} #1}} since the text before and after should be unescaped. But judging from the other post it looks pretty hopeless then. Thanks for the advice. – plauer May 10 '20 at 10:23
  • Just so I understand: What is your use case for \mymacro? With \newcommand\mymacro{\lstinline[escapechar=§]} you could say \mymacro|normal verb §\escaped§ verb again|. But I guess the same could be achieved by setting escapechar=§ globally and using just \lstinline|normal verb §\escaped§ verb again|. – moewe May 10 '20 at 10:30
  • Hey the idea behind it was that I don't have to write normal verb and verb again over and over again since it will always be the same. (And if I desire to change the according strings I wouldn't need to substitute every occurence.) – plauer May 10 '20 at 10:38
  • Ah I see, that's going to be tricky. In that case it might be easier to escape normal verb and verb again manually as in: \newcommand\mymacro[1]{{\ttfamily\color{blue} normal\_verb with\# evil chars #1 verb again \&\_ more stuff}}. (Of course you'd have to manually do syntax highlighting, so that might be a non-starter.) – moewe May 10 '20 at 10:49

1 Answers1

2

If you really want to have this done via the listings-package:

I don't know what TeX-engine you use (whether it is based on traditional TeX with single-byte/8bit-ASCII as internal character-representation scheme or it is based on XeTeX/LuaTeX with multi-byte-utf8/unicode as internal character-representation scheme).

Therefore in the example below the single-byte -encoding latin1 is specified as input-encoding explicitly. If you wish to copy-paste the example for testing, make sure that the resulting text-file on your platform also is latin1-encoded. (Or adjust the call to the inputenc-package to that 8bit/single-byte-encoding in use on your platform.)


As long as \mymacro does always get its arguments by reading and tokenizing portions of the .tex-input-file and does never get things passed as arguments by other macros (whereby things probably got tokenized under the wrong category-code-régime when the arguments were gathered for those other macros before passing them on to \mymacro), you can have \mymacro read and tokenize its arguments under verbatim-catcode-régime and then pass them, inclusive the call to \lstinline, to \scantokens.

For having \mymacro read and tokenize its arguments under verbatim-catcode-régime I use the +v-argument-type provided by the xparse-package.

The process is straightforward:

\mymacro inside a newly opened local scope changes some category-codes and TeX-parameters. Then it calls \innermymacro. \innermymacro gathers three arguments under verbatim-catcode-régime and surrounds them with phrases (phrase 1: \lstinline[escapechar=§]|, phrase 2: §, phrase 3: |%), which at the time of defining \innermymacro also were gathered under verbatim-catcode-régime. This way you get a sequence in terms of tokens in verbatim-catcode-régime
\lstinline[escapechar=§]|<Argument 1>§<Argument2>§<Argument3>|%.
This sequence is passed on to \scantokens, prefixed by \endgroup so that the first thing that happens at the time when \scantokens re-tokenizes and processes things will be closing the local scope.

\documentclass{article}

% You need some 8bit-encoding with the listings-package:
\usepackage[latin1]{inputenc}
% xparse's verbatim-arguments are handy:
\usepackage{xparse}%
% To allow patching commands:
\usepackage{etoolbox}
% To typeset code-listings:
\usepackage{listings}
% To allow escapechar inside lstinline:
\makeatletter
\patchcmd{\lsthk@TextStyle}{\let\lst@DefEsc\@empty}{}{}{\errmessage{failed to patch}}
\makeatother
% For highlighting:
\usepackage{xcolor}

% The example macro's syntax:
%
% \mymacro{<stuff inside \lstinline before the LaTeX-escape>}%
%         {<stuff inside \lstinline inside the LaTeX-escape>}%
%         {<stuff inside \lstinline after the LaTeX-escape>}%
%
\newcommand*\mymacro{%
  \begingroup
  \catcode`\^^I=12\relax
  \catcode`\^^M=12\relax
  \newlinechar=\endlinechar\relax
  \innermymacro
}%
\NewDocumentCommand\innermymacro{+v+v+v}{%
  \RenewDocumentCommand\innermymacro{+v+v+v}{%
     \scantokens{\endgroup#1##1#2##2#2##3#3}%
  }%
}%
\innermymacro{\lstinline[escapechar=§]|}{§}{|%}%

%for demonstration purposes
\def\perfectly{perfectly}

%highlight lstinline
\lstset{
    basicstyle=\ttfamily\color{blue}
}

\begin{document}
\lstinline[escapechar=§]|works §\perfectly§| -- listinline no more active

\mymacro{Before \LaTeX-escape. }%
        {{\frenchspacing Inside \LaTeX-escape: \perfectly.}}%
        { After \LaTeX-escape.} -- lstinline no more active

\end{document}

enter image description here

Ulrich Diez
  • 28,770