[Tex/LaTex] Pretty-printing LaTeX syntax with lstlisting environment

listings

I would like this:

\begin{lstlisting}
  \mymacro[<optional argument>]{<mandatory argument>}
\end{lstlisting}

to generate output like this:

Desired output

I've made a couple attempts at this, but haven't succeeded yet.

Partial solutions

Here's a complete test document that shows what I've tried so far:

\documentclass{article}

% Macros for the syntax environment
\newcommand*{\cs}[1]{{\ttfamily\char`\\#1}}
\newcommand*{\meta}[1]{{\ensuremath{\langle}\rmfamily\itshape#1\/\ensuremath{\rangle}}}
\newcommand*{\oarg}[1]{{\ttfamily[\meta{#1}]}}
\newcommand*{\marg}[1]{{\ttfamily\char`\{\meta{#1}\char`\}}}
\newenvironment{syntax}{\par\medskip\bgroup\obeyspaces\strut}{\egroup\medskip}

\usepackage{listings}

% Shortcut for verbatim code
\lstMakeShortInline[basicstyle=\ttfamily]|

% First attempt to pretty-print the syntax
\lstdefinestyle{first}{%
  columns=fullflexible,
  basicstyle=\ttfamily,
  moredelim={[is][\meta]{<}{>}},
}

\lstdefinestyle{second}{%
  columns=fullflexible,
  basicstyle=\ttfamily,
  literate=*{<}{{$\langle$\itshape}}{1}{>}{{\/$\rangle$}}{1},
}

\begin{document}

\parindent0pt

\section{The problem}

I would like this:

\begin{lstlisting}
  \mymacro[<optional argument>]{<mandatory argument>}
\end{lstlisting}%

to generate output like this:

\begin{syntax}
  \cs{mymacro}\oarg{optional argument}\marg{mandatory argument}
\end{syntax}

\section{Partial solutions}

The first attempt uses the |moredelim| key:

\begin{lstlisting}[style=first]
  \mymacro[<optional argument>]{<mandatory argument>}
\end{lstlisting}

It looks like the |\meta| macro is applied to each ``word'' instead of the entire text delimited by |<| and |>|.

\medskip

The second attempt uses the \texttt{literate} key:
\begin{lstlisting}[style=second]
  \mymacro[<optional argument>]{<mandatory argument>}
\end{lstlisting}

This successfully replaces the |<| and |>| characters, but doesn't apply the |\itshape| formatting.

Any suggestions? Thanks!

\end{document}

Best Answer

It works with delimiters, if you use moredelim with the ** option. In this case, the delimiter macro is invoked not around every single word, but at the begin of the group that is made out of the delimited text. You just have to grab the group's content into a lrbox to typeset the < and > around it:

\documentclass{article}

% Macros for the syntax environment
\newenvironment{syntax}{\par\medskip\bgroup\obeyspaces\strut}{\egroup\medskip}

\makeatletter
\newenvironment{hl@env}
{\begin{lrbox}{\@tempboxa}\rmfamily\itshape}
{\end{lrbox}\ensuremath{\langle}\usebox{\@tempboxa}\kern.3ex\ensuremath{\rangle}}

\newcommand\boxmeta{%
  \begin{hl@env}\bgroup\aftergroup\hl@endenv%
}
\def\hl@endenv{%
  \end{hl@env}%   
  \egroup
}
\makeatother

\usepackage{listings}

% Shortcut for verbatim code
\lstMakeShortInline[basicstyle=\ttfamily]|

% First attempt to pretty-print the syntax
\lstdefinestyle{first}{%
  columns=fullflexible,
  basicstyle=\ttfamily,
  moredelim=**[is][\boxmeta]{<}{>},
}


\begin{document}

\parindent0pt

\section{The problem}

I would like this:

\begin{lstlisting}
  \mymacro[<optional argument>]{<mandatory argument>}
\end{lstlisting}%

to generate output like this:

\begin{syntax}
  \cs{mymacro}\oarg{optional argument}\marg{mandatory argument}
\end{syntax}

\section{Working solution}

The first attempt uses the |moredelim=**| key:

\begin{lstlisting}[style=first]
  \mymacro[<optional argument>]{<mandatory argument>}
\end{lstlisting}

This works :-)


\end{document}

enter image description here