I have been using this solution found here at LaTeX Stack Exchange for colouring numbers in my code excerpts:
\documentclass{article}
\usepackage{listings}
\usepackage{xcolor}
\makeatletter
%%% Copied from https://tex.stackexchange.com/a/500690/23765
% Some conditional tests
\def\@genericif#1{#1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi}
\def\@ifdigit#1{\@genericif{\ifnum1<1\noexpand#1\relax}}
\def\@ifempty#1{\@genericif{\if\relax\detokenize{#1}\relax}}
% The main parsing macros
\def\parse@num#1{%
\@ifempty{#1}%
{\parse@num@false}%
{\@genericif{\parsesign}%
{\parse@num@sign#1{}\@end}%
{\parse@num@dig#1{}\@end}%
}%
}
% Parse sign
\def\parse@num@sign#1#2\@end{%
\@genericif{\ifx\parse@num@minus#1}%
{\@ifempty{#2}{\parse@num@false}{\parse@num@dig#2\@end}}%
{\@genericif{\ifx\parse@num@plus#1}%
{\@ifempty{#2}{\parse@num@false}{\parse@num@dig#2\@end}}%
{\parse@num@dig#1#2\@end}%
}%
}
% Parse first digit
\def\parse@num@dig#1#2\@end{%
\@ifdigit{#1}%
{\@ifempty{#2}{\parse@num@true}{\parse@num@digs#2\@end}}%
{\parse@num@false}%
}
% Parse optional following digits
\def\parse@num@digs#1#2\@end{%
\@ifdigit{#1}{%
\@ifempty{#2}%
{\parse@num@true}%
{\parse@num@digs#2\@end}%
}{%
\@genericif{\parsefloat}{%
\@genericif{\ifx\parse@num@point#1}%
{\@ifempty{#2}{\parse@num@false}{\parse@num@decs#2\@end}}%
{\parse@num@false}%
}{\parse@num@false}%
}%
}
% Parse decimal places
\def\parse@num@decs#1#2\@end{%
\@ifdigit{#1}{%
\@ifempty{#2}%
{\parse@num@true}%
{\parse@num@decs#2\@end}%
}{\parse@num@false}%
}
% User interface
\newcommand\ifnumber[4][]{%
\begingroup
\let\parsesign=\iftrue
\let\parsefloat=\iftrue
\let\parse@num@minus=-%
\let\parse@num@plus=+%
\let\parse@num@point=.%
#1%
\def\parse@num@true{\endgroup#3}%
\def\parse@num@false{\endgroup#4}%
\parse@num{#2}%
}
%%% Additions to the listings package
\lst@Key{numbersstyle}{}{\def\lst@numbersstyle{#1}}
\lst@Key{parsenumbers}{false}[t]{\lstKV@SetIf{#1}\lst@ifparsenumbers}
\lst@AddToHook{OutputOther}{%
\lst@ifparsenumbers
% Only if mode changes are not prohibited
\lst@ifmode\else
\expandafter\@hook@ifnumber\the\lst@token\@end
{\let\lst@thestyle=\lst@numbersstyle}%
{}%
\fi
\fi
}
\def\@hook@ifnumber#1#2\@end{%
\@genericif{\ifx\lst@nolig#1}%
{\@hook@ifnumber@{#2}}%
{\@hook@ifnumber@{#1#2}}%
}
\def\@hook@ifnumber@{%
\ifnumber[\expandafter\let\expandafter\parse@num@minus\csname lst@um-\endcsname]%
}
\makeatother
%%% Example document
\lstset{
basicstyle = \ttfamily,
identifierstyle = \color{blue},
keywordstyle = \color{green!80!black},
keywords = {foo},
moredelim = [il][]{**},
moredelim = [l][\color{gray}]{/},
morestring = [d][\color{gray}]{"},
morestring = *[d][\color{gray}\itshape]{!},
morestring = **[d][\color{gray}\itshape]{?},
% Apply new number coloring routine
parsenumbers = true,
numbersstyle = {\color{magenta}}
}
It works fairly well but unfortunately it also does have some issues. Appending this above my minimal example below...:
\begin{document}
\section{Python}
\begin{lstlisting}
def foobar(self):
var = 123 + 456
var_2 = 4.56
var3 = 789
for _ in range(3):
print(test)
if var_2 > 1.23:
print(1024)
elif (var3 <= 1000 and var_2 is None):
print(0)
\end{lstlisting}
\section{Processing}
\begin{lstlisting}
void setup() {
size(300, 300);
background(0, 200, 0);
}
void draw() {
drawFlower(150, 150, 100);
for (int i = 0; i < 80; i = i+5) {
line(30, i, 80, i);
}
x = x + 0.1;
y = 0.1 + y;
if (x > 1.23) {
x = 0;
y = 0 ;
}
}
\end{lstlisting}
\end{document}
...results in:
The results are not bad, and it's particularly good that that solution handles numbers in variable names regardless if they are followed by an underline or not (e.g. var_2
and var3
). But the example above also show a couple of problems which I have not been able to fix:
- It's possible to see that numbers preceded by a
(
or immediately followed by)
are not coloured, e.g.range(3)
,size(300, 300)
- numbers touching signs such as commas, colons, or semicolons are not coloured either; e.g. the middle number in
background(0, 200, 0);
, or the numbers inx = x + 0.1;
orif var_2 > 1.23:
- on the other hand, having spaces around them result in proper highlight, e.g.
y = 0 ;
(with space before semicolon) or the number1000
inelif (var3 <= 1000 and var_2 is None):
Would anyone be able to help me tweak this snippet so that the numbers are consistently highlighted in these situations while still not being highlighted in variable and function names?
Edit: in a nutshell, I would like numbers to be highlighted if:
- they come after any of the following characters:
.
,(
,{
,[
,:
- they come before any of the following characters:
.
,)
,}
,]
,:
,;
- but definitely not include
_
in either group as that might break the variable naming convention in many snippets of code (even though Python does accept_
in numbers to aid the visual identification of groups of 10^3, as inx = 1_000_000
.
Currently, the code above only correctly identifies
and .
from all the characters mentioned in the bullet points above.
Edit: unfortunately minted
is not an option for me, it does not play along well with my dissertation file.