Предположим, я хочу набрать сеанс экрана на каком-то языке программирования (в моем случае это GAP, но это может быть Python, BASH или что-то еще с интерактивным приглашением для чтения). Таким образом, на моем экране может быть что-то вроде этого:
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>
То есть, есть командная строка gap>
, после которой пользователь вводит многострочный ввод. После этого следует строка вывода, затем еще одна строка приглашения gap>
. Это повторяется, пока команда не вызовет ошибку, после чего приглашение изменится на brk>
.
Я хотел бы использоватьlistings
пакет (или что-то еще) для
- раскрасьте подсказки
gap>
,>
, иbrk>
; скажем, синим и красным; - выделите ключевые слова, например
for
,do
, иod
; например, сделав их жирным шрифтом; - нотольковыделить ключевые слова в строках, начинающихся с подсказки, следовательно, не внутри вывода моих команд.
Последний пункт — это тот, который вызывает у меня проблемы. Я не смог придумать, как это сделать. Результат часто бывает довольно уродливым, когда слова типа and
, not
, и in
выделяются как ключевые слова, например, в сообщении об ошибке.
Похожие вопросы уже задавались, но из тех, что мне удалось найти, ни один не содержал именно этого, соответственно, ни один не получил ответа, который делает то, что мне нужно.
Для справки, вот определение языка, которое я сейчас использую:
\lstdefinelanguage{GAP}{% morekeywords=[2]{и,прервать,продолжить,сделать,elif,иначе,конец,неудача,ложь,fi,для,% функция, если, в, локальном, mod, не, od, или, rec, повтор, возврат, то, истина, % пока},% moredelim=[s][\color{blue}]{gap}{>},% moredelim=[s][\color{red}]{brk}{>},% %moredelim=*[l][\color{blue}]{gap>},% %moredelim=*[l][\color{red}]{brk>},% чувствительный=истина,% morecomment=[l]\#,% больше строк=[b]',% больше строк=[b]",% }%
PS: Чтобы проиллюстрировать то, что я хочу, рассмотрите это изображение, показывающее, как может выглядеть мой пример сверху (в дополнение к тому, что я описал выше, я также выделил все пользовательские вводы курсивом):
решение1
Этот listings
ответ запоздал примерно на год, но я считаю, что он охватывает все ваши требования.
Редактировать: исправлена ошибка, связанная с появлением подсказок «ключевые слова» в середине строки.
Желаемый результат
Выход получен
Код
\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}
решение2
Единственный недостаток в том, что я обрабатываю целые слова, а не подслова. Таким образом, od;
это отдельное слово изod
. Это можно преодолеть, но не в этом MWE. Я скопировал ваш сырой (неотформатированный) вывод сеанса в файлсессия.ви пошло оттуда.
\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}