# How can I highlight an interactive GAP session just like so?

Suppose I want to typeset a screen session in some programming language (GAP in my case, but it could be Python, BASH, or something else with an interactive read prompt just as well). So on my screen, I might have something like this:

gap> for i in [1..10] do
>   Print(i, ":");
> od;
1:2:3:4:5:6:7:8:9:10:
gap> 2^10;
1024
gap> 1/0;
Error, Rational operations: <divisor> must not be zero
not in any function at line 5 of *stdin*
you can replace <divisor> via 'return <divisor>;'
brk> 2^10;
1024
brk>


That is, there is a command prompt gap>, after which the user enters a multi line input. After that comes a line of output, then another prompt gap>. This repeats, until a command triggers an error, at which point the prompt changes to brk>.

I would like to use the listings package (or something else) to

1. color the prompts gap>, >, and brk>; say, blue and red;
2. highlight keywords like for, do, and od; say, by making them bold;
3. but only highlight the keywords in lines starting with a prompt, hence not inside the output of my commands.

The last point is the one that is causing me troubles. I couldn't figure out a way to do that. The result is often quite ugly when words like and, not, and in are highlighted as keywords in, e.g., an error message.

Similar questions have been asked before, but of those I managed to find, none asked for exactly this, resp. got an answer that does what I want.

For the record, here is the language definition I am using for now:

\lstdefinelanguage{GAP}{%
morekeywords=[2]{and,break,continue,do,elif,else,end,fail,false,fi,for,%
function,if,in,local,mod,not,od,or,rec,repeat,return,then,true,%
until,while},%
moredelim=[s][\color{blue}]{gap}{>},%
moredelim=[s][\color{red}]{brk}{>},%
%moredelim=*[l][\color{blue}]{gap>},%
%moredelim=*[l][\color{red}]{brk>},%
sensitive=true,%
morecomment=[l]\#,%
morestring=[b]',%
morestring=[b]",%
}%


PS: To illustrate what I want, consider this image showing how my example from above could look (in addition to what I described above, I also set all user inputs in italics):

• This is a duplicate of my question tex.stackexchange.com/questions/73893/… Apr 18, 2013 at 16:24
• Hm, the answer to your question proposes to use mordelim as a solution -- if you look, I am already using it. But I don't see how that can overcome point 3 of my requirements, and the example picture in the solution to your question seems to indicate that it does not satisfy point 3, as "ans" in one of the output lines is printed in bold. But I need the keyword formatting to be only applied to inputs, not outputs. Apr 18, 2013 at 22:02
• Hm, you are right, sorry for the confusion. Apr 19, 2013 at 7:00
• Your specs are somewhat contradictory. For instance, in the text, you require the keywords to be bold, but they appear medium and green in your screenshot. Also, whether you actually want the user input to be slanted is unclear. Could you clarify, maybe by listing all your specs in one place? Mar 1, 2014 at 20:06
• Yes, the spec says "keywords = bold" and the example instead makes them green -- either is fine, based on the assumption that once I know how to do it, I could change it suitably :-). The reason the "spec" and the screenshot differ comes from them being created at different times. Mar 2, 2014 at 14:42

This listings answer is about a year late, but I believe it covers all your requirements.

Edit: fixed a bug related to prompt "keywords" occurring in the middle of a line.

## Code

\documentclass[a4paper]{article}
\usepackage{xcolor}
\usepackage{textcomp}
\usepackage{listings}

\makeatletter

% switch to remember whether a prompt was found on the current line
\newif\ifprompt@GAP@
% switch to flag whether a prompt keyword' is an bona fide GAP prompt
\newif\iftoolateforprompt@GAP@

% style of GAP keywords
\definecolor{GAPkeywords}{RGB}{077,145,021}
\newcommand\GAPkeywordstyle{\color{GAPkeywords}}

% font shape of GAP input lines
\newcommand\GAPinputfshape\slshape

\lstdefinelanguage{GAP}
{%
basicstyle =\ttfamily,
alsoletter=>,
morekeywords=[2]{%
and,break,continue,do,elif,else,end,fail,false,fi,for,function,if,in,%
local,mod,not,od,or,rec,repeat,return,then,true,until,while,
},%
keywordstyle=[2]\Process@GAP@keywords,
morekeywords=[3]{gap>,>},
keywordstyle=[3]\Process@GAP@prompt{blue},
morekeywords=[4]{brk>},
keywordstyle=[4]\Process@GAP@prompt{red},
sensitive=true,%
upquote=true,% <--- for straight single quotes
showstringspaces=false,
morecomment=[l]\#,
morestring=[b]',
morestring=[b]",
}%

% only highlight keywords if a prompt has occured on the current line
\newcommand\Process@GAP@keywords{\ifprompt@GAP@\GAPkeywordstyle\fi}

\newcommand\Process@GAP@prompt[1]
{%
\iftoolateforprompt@GAP@%
\else%
\color{#1}\upshape% customise the style of your > and gap> prompts here
\global\prompt@GAP@true%
\aftergroup\GAPinputfshape% we trigger slanted shape after > or gap>
\fi%
}

% Hook into InitVarsEOL (some hook executed right after an EOL) to:
% - reset the \ifprompt@GAP@ and \iftoolateforprompt@GAP@ switches
% - reset the font shape to \upshape
{%
\global\prompt@GAP@false%
\global\toolateforprompt@GAP@false%
\upshape%
}

% Hook into PostOutput to signal that a GAP prompt
% can no occur on the current line

\makeatother

\begin{document}
\begin{lstlisting}[language=GAP]
gap> for i in [1..10] do
>   Print(i, ":");
> od;
1:2:3:4:5:6:7:8:9:10:
gap> 2^10;
1024
gap> 1/0;
Error, Rational operations: <divisor> must not be zero
not in any function at line 5 of *stdin*
you can replace <divisor> via 'return <divisor>;'
brk> 2^10;
1024
brk>
\end{lstlisting}
\end{document}

• Pretty cool! I'll give it a closer look later. In the meantime, one its tiny issue I noticed: If there are extra whitespace between prompt and commands then the highlighting does not work (I tried to add an example, but found to way to make multiple spaces in a row visible in a comment). Anyway, hopefully that would be trivial to change? Mar 2, 2014 at 16:36
• @Max See my edit. I'm not sure why, but it seems that using the InitVarsEOL hook (instead of the EveryLine hook) solves problem. Mar 2, 2014 at 16:58
• Great, that works better. But there is another odd thing: If there is output which contains something that looks like a string, say "bla", then a subsequent > is treated as if it started a prompt. Likewise, a single " or ' throws the code off. So, I wonder, can one get it to not ignore string delimiters in output text? Just like you made it ignore (or rather: not color) keywords there? Seems harder to achieve... Mar 2, 2014 at 18:51
• @Max See my edit regarding the first problem you report; I think I fixed it. I'll look into the second problem (single " or '), but it looks more complicated indeed. Mar 2, 2014 at 19:12
• @Max Unfortunately, I can't think of an easy way to prevent " and ' from starting a string on your "output lines". Perhaps I'll think of something when I'm more familiar with listings... in a couple of years! Mar 2, 2014 at 19:19

The one drawback is that I process whole words, not subwords. Thus, od; is a separate word from od. That can be overcome, but not in this MWE. I copied your raw (unformatted) session output into the file session.in and went from there.

\documentclass[12pt]{article}
\makeatletter%
\let\protectededef\protected@edef
\makeatother%

\parindent 0in
\renewcommand{\encodingdefault}{T1}
\usepackage{color}
\usepackage{verbatimbox}
\usepackage{ifthen}
\catcode^=12
\definecolor{darkgreen}{rgb}{0,0.5,0}
\newcounter{rowindex}\newcounter{wordindex}%

\newcommand\displaysource[1]{%
\sffamily%
\setcounter{rowindex}{0}%
\whiledo{\value{rowindex} < \nrecords}{%
\getargsC{\csname record\roman{rowindex}\endcsname}%
\ifthenelse{\equal{\argi}{>}  \OR%
\equal{\argi}{gap>}  \OR%
\equal{\argi}{brk>}}%
{\def\userin{\itshape}}{\def\userin{\upshape}}%
\setcounter{wordindex}{0}%
\whiledo{\value{wordindex} < \narg}{%
\protectededef\thisword{\csname arg\roman{wordindex}\endcsname}%
\ifthenelse{\equal{\thisword}{gap>}  \OR%
\equal{\thisword}{>}}%
{%
\upshape\textcolor{blue}{\thisword~}%
}{%
\ifthenelse{\equal{\thisword}{brk>}}%
{%
\textcolor{red}{brk>~}%
}{%
\userin%
\ifthenelse{\equal{\thisword}{for}  \OR%
\equal{\thisword}{in}  \OR%
\equal{\thisword}{do}  \OR%
\equal{\thisword}{od}  \OR%
\equal{\thisword}{od;}}%
{%
\ifthenelse{\equal{\userin}{\itshape}}%
{%
\textcolor{darkgreen}{\thisword~}%
}{%
\thisword~%
}%
}{%
\thisword~%
}
}%
}%
}%
\\%
}%
\rmfamily%
}
\begin{document}

\displaysource{session.in}

\end{document}