5

My goal is to be able to make font changes to arbitrary selections of text within a lstlisting environment by escaping to LaTeX and constructing the new font in the escaped section (is there a smarter way to make such font changes than such an approach?). However, I would like to be able to maintain a verbatim-like environment in the escaped section, in the sense that spaces are maintained and special characters don't cause a problem.

So in other words, I would like to create a macro altfont to do something like the following:

\begin{lstlisting}[escapeinside={(*@}{@*)}]
Some text here and (*@\altfont{now a change in font}@*) and now back to
the regular font.
\end{lstlisting}

My first thought was to use a call to lstinline in the escaped section, but unfortunately this is not allowed according to the listings documentation. Using verb in this setting caused an error to be raised during compilation for me as well.

So I've tried to create my own macro to do the job as shown in the example below, but I can't quite achieve the desired effect. One problem is that I'm having is getting the macro to respect spaces, and the second problem is that some special characters cause the macro to choke (e.g. the % character).

Hopefully the example below will further illustrate what I'm trying to do.

\documentclass{article}

\usepackage{listings}
\usepackage{xcolor}

% sets the font for lstlisting environments to a monospace font, and sets an
% escape sequence that can be used to inject arbitrary LaTeX code into an
% lstlisting environment
\lstset{%
  basicstyle=\fontfamily{pcr}\selectfont,
  escapeinside={(*@}{@*)}
}

% create an alternative font to use for escaped sequences inside lstlisting
% environments
\newcommand{\altfont}[1]{%
  \fontfamily{pcr}\selectfont{\color{blue}\obeyspaces\detokenize{#1}}
}


\begin{document}

% target spacing
\begin{lstlisting}
this text
     is
     properly
     aligned
\end{lstlisting}

% using the contents of `altfont` handles spaces properly
\begin{lstlisting}
this text
     is
(*@\fontfamily{pcr}\selectfont{\color{blue}\obeyspaces\detokenize{     also}}@*)
     aligned
\end{lstlisting}

% the macro itself eats the spaces
\begin{lstlisting}
this text
     is
(*@\altfont{     not}@*)
     aligned
\end{lstlisting}


\end{document}

Output from the compiled example:

enter image description here

2
  • Have you tried putting the spaces before the escape sequence?
    – TeXnician
    Commented Mar 3, 2018 at 18:42
  • 1
    @TeXnician Placing the spaces outside of the escape sequence would certainly work for this particular example. In general though you might wish to have multiple strings delimited by whitespace, in which case you would have to have an escape sequence for each string, which would be less than ideal. On the other hand, this will have to be my workaround though if I can't figure out why the macro is eating the extra spaces!
    – dpritch
    Commented Mar 3, 2018 at 19:53

1 Answer 1

5

Multiple spaces are collapsed when TeX first reads the spaces and fixes their catcode. This happens when the macro is called, before the content is evaluated, because TeX has to read the argument. At this point \obeyspaces isn't evaluated yet, so it has no effect.

This can be fixed by moving \obeyspaces before the scan of the argument, so you can replace your definition of \altfont with

\newcommand{\altfont}{%
  \begingroup%
  \catcode`\\=12\relax%
  \catcode`\%=12\relax%
  \obeyspaces%
  \expandafter\endgroup\altfontHelper%
}
\newcommand{\altfontHelper}[1]{%
  {\fontfamily{pcr}\selectfont\color{blue}{}\detokenize{#1}}%
  \endgroup%
}

The \catcode lines are used to allow \ and % to be used in a verbatim-like way.

Defining commands with verbatim arguments gets easier with xparse. With this package, your document becomes

\documentclass{article}

\usepackage{listings}
\usepackage{xcolor}
\usepackage{xparse}

% sets the font for lstlisting environments to a monospace font, and sets an
% escape sequence that can be used to inject arbitrary LaTeX code into an
% lstlisting environment
\lstset{%
  basicstyle=\fontfamily{pcr}\selectfont,
  escapeinside={(*@}{@*)}
}

% create an alternative font to use for escaped sequences inside lstlisting
% environments
\NewDocumentCommand\altfont{v}{%
  {\fontfamily{pcr}\selectfont\color{blue}{}#1}
}

\begin{document}

% target spacing
\begin{lstlisting}
this text
     is
     properly
     aligned
\end{lstlisting}

% using the contents of `altfont` handles spaces properly
\begin{lstlisting}
this text
     is
(*@\fontfamily{pcr}\selectfont\obeyspaces\color{blue}{}     also@*)
     aligned
\end{lstlisting}

% the macro itself no longer eats the spaces
\begingroup\obeyspaces%
\begin{lstlisting}
this text
     is
(*@\altfont{     now also}@*)
     aligned
\end{lstlisting}
\endgroup%

\end{document}
1
  • This answer is fantastic, thanks! Just an observation, it doesn't seem necessary to include the \begingroup\obeyspaces% and \endgroup% commands around the lstlisting environment, for this example at least. I can't speak as to whether it's necessary for the general case though.
    – dpritch
    Commented Mar 5, 2018 at 3:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .