23

I'm trying to typeset some python code, and I would like integer literals to be highlighted. I've come up, after about two hours of fighting with the documentation and various other questions here and on SO, with the following:

\lstset{ %
    language=Python,
    otherkeywords={1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
    morekeywords=[2]{1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
    keywordstyle=[2]{\color{orange}},
    keywordstyle=\color{blue}\bfseries,
    stringstyle=\color{red},
}

Unfortunately, that doesn't work. Numbers found in strings are also highlighted, which is rather annoying, and not only are they highlighted: they are highlighted in blue. Numerals in regular program text, on the other hand, are rightly highlighted in red.

Help?

EDIT: Thanks Peter Grill for your very nice solution. Is there a way to not break string colouring, though? When I use you code, the stringstyle=\color{red} doesn't seem to work anymore (strings are just plain black). Similarly, comments can be enclosed in triple quotes in python, and if I adapt your quote to detect """ blocks, then the corresponding comments are not highlighted anymore.

Clément
  • 4,004

2 Answers2

21

It seems that you want the coloring of the digits, but not when they are within a string. If I correctly understand, you can use a conditional to define if we should apply the color or not. Then when we encounter a quote, we disable to coloring until the next quote.

Here I have disabled coloring within both single and double quotes. If this is not desired, simply comment out the corresponding line in \lstset.

enter image description here

Below I have used newtoggle from the etoolbox package as I like that syntax much better than the \newif syntax. But if you don't want to include an additional package it should be pretty straightforward to adapt this to use \newif or some other conditional methods.

Further Enhancements:

  • If you desire to extend this to handle floating point numbers, and highlight the decimal separator (but not periods), see Listings package: How can I format all numbers?

  • A further enhancement would be to disable the coloring within comments in the code. However, this is dependent on the particular language being typeset, but a similar technique could be applied to disable coloring at the beginning of a comment and re enable it at the end of the comment. This should be straightforward for comments that have a begin and end delimiter (i.e., /* ... */ C-style comments), but for comments that go to end of line (i.e., // C-style comments), the end of line will need to be detected and used to re enable comments.

Simpler Solution:

Code:

\documentclass{article}
\usepackage{etoolbox}
\usepackage{xcolor}
\usepackage{listings}

\newtoggle{InString}{}% Keep track of if we are within a string \togglefalse{InString}% Assume not initally in string

\newcommand{\ColorIfNotInString}[1]{\iftoggle{InString}{#1}{\color{red}#1}}% \newcommand{\ProcessQuote}[1]{#1\iftoggle{InString}{\global\togglefalse{InString}}{\global\toggletrue{InString}}}% \lstset{literate=% {"}{{{\ProcessQuote{"}}}}1% Disable coloring within double quotes {'}{{{\ProcessQuote{'}}}}1% Disable coloring within single quote {0}{{{\ColorIfNotInString{0}}}}1 {1}{{{\ColorIfNotInString{1}}}}1 {2}{{{\ColorIfNotInString{2}}}}1 {3}{{{\ColorIfNotInString{3}}}}1 {4}{{{\ColorIfNotInString{4}}}}1 {5}{{{\ColorIfNotInString{5}}}}1 {6}{{{\ColorIfNotInString{6}}}}1 {7}{{{\ColorIfNotInString{7}}}}1 {8}{{{\ColorIfNotInString{8}}}}1 {9}{{{\ColorIfNotInString{9}}}}1 } \begin{document} \begin{lstlisting}[basicstyle=\ttfamily] These are colored 0123456789, but "these are not 0123456789" and 'these also are not 0123456789' again colored: 0123456789 \end{lstlisting} \end{document}

Peter Grill
  • 223,288
  • I think it's a great idea to have a toggle. However, I would image such a toggle to not be printed in the text since it merely represents a toggle. As such, it would probably some character that is not often used in the language. This is user-specific though. – Werner Nov 15 '11 at 19:01
  • @Werner: Sorry, I do not understand what you mean by "such a toggle to not be printed in the text"? The intent here was to exclude coloring of digits in strings, so " defines the begin and end of a string. – Peter Grill Nov 15 '11 at 19:08
  • Mmmm, I thought strings here might refer to keywords like myvar1 or func123 and the like. I may have been mistaken. – Werner Nov 15 '11 at 19:10
  • @Werner: Good point. I would think that that would be covered by keywords. Another reason why we need a MWE to clarify questions. – Peter Grill Nov 15 '11 at 19:13
  • 1
    Wow. Thanks for this thorough and detailed answer. I'll try it immediately. – Clément Nov 16 '11 at 16:08
  • Works very nice! Although, is there a way to also highlight strings? – Clément Nov 17 '11 at 05:25
  • Not sure what you mean by "highlight string"? If this is directly related to this question you should update the question and provide a MWE. If it is not about coloring digits, then it should be a follow up question. You can refer to this question in that and explain what else you are looking for. – Peter Grill Nov 17 '11 at 15:24
  • @PeterGrill: I've edited the question accordingly. – Clément Nov 20 '11 at 11:56
  • @Clément: Did this solution not answer your original question? – Peter Grill Jan 06 '12 at 19:17
  • @PeterGrill: You can do this more easily by adding a simple *. See http://tex.stackexchange.com/a/42895/9043 – qubyte Jan 31 '12 at 16:04
  • @MarkS.Everitt: Thanks. I did not know about the * option and have added a link here to that question. – Peter Grill Jan 31 '12 at 17:18
  • @PeterGrill: The problem with your answer was that it disabled string colouring; numbers were coloured, but strings lost their highlighting. – Clément Jan 31 '12 at 18:37
  • 1
    @Clément: Sorry, I did not understand what you had wanted earlier (seem obvious now that I see the other question). That is one reason why it is always best to compose a fully compilable MWE instead of "describing" the symptom. – Peter Grill Jan 31 '12 at 18:42
  • @Peter Grill: Amazing, thanks! I am trying to work on the enhancement with the comments, as you outlined it above. My question is here, please take a look! :) – Jonas Jun 16 '13 at 17:31
10

Here is a way of applying a style to digits (only outside strings and comments), that improves upon Peter Grill's approach in two ways.

  1. It is more flexible insofar as it allows you to specify additional replacements (of <= by $\leq$, for instance) to take effect everywhere in the code; that's handy because, in practice, you may want at least some literate replacements to also take place in comments (in particular) and in strings.
  2. It is more robust insofar as it will work "out of the box", no matter how you define your string and comment delimiters.

enter image description here

\documentclass{article}

\usepackage{xcolor}
\usepackage{listings}

\newcommand\digitstyle{\color{red}}
\makeatletter
\newcommand{\ProcessDigit}[1]
{%
  \ifnum\lst@mode=\lst@Pmode\relax%
   {\digitstyle #1}%
  \else
    #1%
  \fi
}
\makeatother
\lstset{literate=
    {0}{{{\ProcessDigit{0}}}}1
    {1}{{{\ProcessDigit{1}}}}1
    {2}{{{\ProcessDigit{2}}}}1
    {3}{{{\ProcessDigit{3}}}}1
    {4}{{{\ProcessDigit{4}}}}1
    {5}{{{\ProcessDigit{5}}}}1
    {6}{{{\ProcessDigit{6}}}}1
    {7}{{{\ProcessDigit{7}}}}1
    {8}{{{\ProcessDigit{8}}}}1
    {9}{{{\ProcessDigit{9}}}}1
    {<=}{{\(\leq\)}}1,
    morestring=[b]",
    morestring=[b]',
    morecomment=[l]//,
}
\begin{document}
\begin{lstlisting}[basicstyle=\ttfamily]
    These are colored 0123456789,
    but "these are not 0123456789"
    and 'these also are not 0123456789'
    again colored: 0123456789
    // not colored: 0123456789
    Additional literate replacement:
    <= "<=" // comment 1 <= 2
\end{lstlisting}
\end{document}
jub0bs
  • 58,916
  • could this somehow be made so that also keywords etc. are spared from being replaced? This apparently is not so in this version. – stefanct Mar 29 '14 at 02:08
  • Yes, my current solution is not ideal because, as you've noticed, keyword containing digits will not be highlighted as such. I believe that answer could be modified to improve this one, but you'd have to specify exactly what it is you want. For instance, would you want to color all numbers (including floating points) or just integers? Should 0x234 be highlighted as number? In any case, the asker apparently got his answer here. You should probably ask a fresh question that lists all your highlighting requirements. – jub0bs Mar 29 '14 at 07:23
  • @Jubobs Could it not be colored after some character, for example, I type A1, then the A1 is not colored red but if I type only 1 then it is colored. – Duy Jun 08 '17 at 23:43