Suponha que eu queira compor uma sessão de tela em alguma linguagem de programação (GAP no meu caso, mas também poderia ser Python, BASH ou qualquer outra coisa com um prompt de leitura interativo). Então, na minha tela, posso ter algo assim:
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>
Ou seja, há um prompt de comando gap>
, após o qual o usuário insere uma entrada multilinha. Depois disso, vem uma linha de saída e, em seguida, outro prompt gap>
. Isso se repete até que um comando acione um erro, momento em que o prompt muda para brk>
.
Eu gostaria de usar olistings
pacote (ou outra coisa) para
- pinte os prompts
gap>
,>
ebrk>
; digamos, azul e vermelho; - destaque palavras-chave como
for
,do
eod
; digamos, tornando-os ousados; - masapenasdestaque as palavras-chave nas linhas que começam com um prompt, portanto, não dentro da saída dos meus comandos.
O último ponto é o que está me causando problemas. Eu não consegui descobrir uma maneira de fazer isso. O resultado costuma ser bastante feio quando palavras como and
, not
e in
são destacadas como palavras-chave, por exemplo, em uma mensagem de erro.
Perguntas semelhantes foram feitas antes, mas daquelas que consegui encontrar, nenhuma perguntou exatamente isso, resp. recebi uma resposta que faz o que eu quero.
Para registro, aqui está a definição de linguagem que estou usando agora:
\lstdefinelinguagem{GAP}{% morekeywords=[2]{e,quebrar,continuar,fazer,elif,else,end,fail,false,fi,for,% função, se, em, local, mod, não, od, ou, rec, repetir, retornar, então, verdadeiro,% até, enquanto},% moredelim=[s][\color{azul}]{gap}{>},% moredelim=[s][\cor{vermelho}]{brk}{>},% %moredelim=*[l][\color{azul}]{gap>},% %moredelim=*[l][\color{vermelho}]{brk>},% sensível=verdadeiro,% mais comentário=[l]\#,% maisstring=[b]',% maisstring=[b]",% }%
PS: Para ilustrar o que quero, considere esta imagem mostrando como poderia ser meu exemplo acima (além do que descrevi acima, também configurei todas as entradas do usuário em itálico):
Responder1
Esta listings
resposta está com cerca de um ano de atraso, mas acredito que atende a todos os seus requisitos.
Editar: corrigiu um bug relacionado ao prompt de "palavras-chave" que ocorria no meio de uma linha.
Saída desejada
Resultado obtido
Código
\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
\lst@AddToHook{InitVarsEOL}%
{%
\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
\lst@AddToHook{PostOutput}{\global\toolateforprompt@GAP@true}
\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}
Responder2
A única desvantagem é que processo palavras inteiras, não subpalavras. Portanto, od;
é uma palavra separada de od
. Isso pode ser superado, mas não neste MWE. Copiei a saída da sessão bruta (não formatada) para o arquivosessão.ine parti daí.
\documentclass[12pt]{article}
\makeatletter%
\let\protectededef\protected@edef
\makeatother%
\parindent 0in
\renewcommand{\encodingdefault}{T1}
\usepackage{color}
\usepackage{readarray}
\usepackage{verbatimbox}
\usepackage{ifthen}
\catcode`^=12
\definecolor{darkgreen}{rgb}{0,0.5,0}
\newcounter{rowindex}\newcounter{wordindex}%
\newcommand\displaysource[1]{%
\sffamily%
\readdef{#1}{\x}%
\setcounter{rowindex}{0}%
\whiledo{\value{rowindex} < \nrecords}{%
\addtocounter{rowindex}{1}%
\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}{%
\addtocounter{wordindex}{1}%
\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}