Listings: Zahlen konsequent einfärben

Listings: Zahlen konsequent einfärben

Ich verwendediese Lösunghier bei LaTeX Stack Exchange zum Einfärben von Zahlen in meinen Codeauszügen gefunden:

\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}}
}

Es funktioniert ziemlich gut, hat aber leider auch einige Probleme. Ich füge dies meinem Minimalbeispiel unten hinzu ...:

\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}

...führt zu:

Bildbeschreibung hier eingeben

Die Ergebnisse sind nicht schlecht, und es ist besonders gut, dass diese Lösung Zahlen in Variablennamen unabhängig davon verarbeitet, ob ihnen ein Unterstrich folgt oder nicht (z. B. var_2und var3). Das obige Beispiel zeigt jedoch auch einige Probleme, die ich nicht beheben konnte:

  • Es ist möglich, dass Zahlen, denen ein vorangestellt ist (oder die unmittelbar darauf folgen, )nicht gefärbt sind, z. B. range(3),size(300, 300)
  • Zahlen, die Zeichen wie Kommas, Doppelpunkte oder Semikolons berühren, werden ebenfalls nicht eingefärbt; z. B. die mittlere Zahl in background(0, 200, 0);oder die Zahlen in x = x + 0.1;oderif var_2 > 1.23:
  • Andererseits führen Leerzeichen um sie herum zu einer korrekten Hervorhebung, z. B. y = 0 ;(mit Leerzeichen vor Semikolon) oder die Zahl 1000inelif (var3 <= 1000 and var_2 is None):

Kann mir jemand helfen, diesen Codeausschnitt so zu optimieren, dass die Zahlen in diesen Situationen durchgängig hervorgehoben werden, in Variablen- und Funktionsnamen jedoch weiterhin nicht?

Bearbeiten: Kurz gesagt, ich möchte, dass Zahlen hervorgehoben werden, wenn:

  • Sie stehen nach einem der folgenden Zeichen: , ., (, {, [,:
  • Sie stehen vor einem der folgenden Zeichen: , ., ), }, ], :,;
  • aber auf keinen Fall _in eine der Gruppen aufnehmen, da dies in vielen Codeausschnitten gegen die Konvention zum Benennen von Variablen verstoßen könnte (obwohl Python _Zahlen akzeptiert, um die visuelle Identifizierung von Gruppen von 10^3 zu erleichtern, wie in ) x = 1_000_000.

Derzeit identifiziert der obige Code nur alle in den Aufzählungspunkten oben genannten Zeichen korrekt ..

Edit: mintedist für mich leider keine Option, da es mit meiner Dissertationsdatei nicht gut funktioniert.

Antwort1

Erwägen Sie die Verwendung mintedund Anpassung der Stylesheets.

Bildbeschreibung hier eingeben

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{minted}

\usemintedstyle{colorful}

\begin{document}

\begin{minted}{python}
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{minted}

\begin{minted}{c}
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{minted}

\end{document}

Antwort2

Wenn ich die Frage richtig verstanden habe, möchten Sie alle Ziffern einfärben, es sei denn, sie sind in Schlüsselwörtern oder Kennungen enthalten. Sie können dies tun, indem Sie als Basisschriftart eine Schriftart verwenden, die farbige Ziffern hat, und eine andere Schriftart für die Schlüsselwörter. Mit lualatex ist dies hier möglich:

\documentclass{article}
\usepackage{listings,xcolor}
\usepackage{fontspec}

\directlua{
 luaotfload.add_colorscheme("colordigits",
   {
    ["FF00FF"] = {"one","two","three","four","five","six","seven","eight","nine","zero"},
   })}

\newfontfamily\colordigits{Latin Modern Mono}[RawFeature={color=colordigits}]

\lstset{
    basicstyle = \colordigits,
    identifierstyle = \ttfamily\color{blue},
    keywordstyle = \ttfamily\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]{?},
}
%
\begin{document}

\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}

Bildbeschreibung hier eingeben

Antwort3

Der Vollständigkeit halber sei erwähnt, dass eine Nicht-Lua-Lösung mit Interchartoks in xetex im letzten Moment fehlschlägt, da lstlistings zwischen den Tokens Kleber einfügt. Kleber wird von Interchartoks als Markierung zwischen Wortgrenzen betrachtet.

Also keine Antwort (ohne eine Neufassung des Pakets (unter Verwendung von Joinern mit Nullbreite?), selbst wenn das PDFLatex-Szenario überwindbar wäre.

Um die Übergänge zwischen Token-Klassen zu veranschaulichen:

Rot ist der Übergang von der Wortgrenze zur Ziffer.

Auflistung Kleber

MWE

\documentclass{article}
\XeTeXinterchartokenstate=1
\usepackage{listings,xcolor}
\usepackage{fontspec}

\newXeTeXintercharclass\LetterClass

%from:
%https://tex.stackexchange.com/questions/411846/xelatex-minion-pro-and-italian-apostrophe-kerning/411850#411850
\makeatletter
\@tempcnta=`\A
\loop\unless\ifnum\@tempcnta>`\Z
  \XeTeXcharclass \@tempcnta \LetterClass
  \advance \@tempcnta by 1
\repeat
\@tempcnta=`\a
\loop\unless\ifnum\@tempcnta>`\z
  \XeTeXcharclass \@tempcnta \LetterClass
  \advance \@tempcnta by 1
\repeat
  \XeTeXcharclass `\_ \LetterClass


\makeatother


% char class for digits
\newXeTeXintercharclass \mydigitsclass
\XeTeXcharclass `\0 \mydigitsclass
\XeTeXcharclass `\1 \mydigitsclass
\XeTeXcharclass `\2 \mydigitsclass
\XeTeXcharclass `\3 \mydigitsclass
\XeTeXcharclass `\4 \mydigitsclass
\XeTeXcharclass `\5 \mydigitsclass
\XeTeXcharclass `\6 \mydigitsclass
\XeTeXcharclass `\7 \mydigitsclass
\XeTeXcharclass `\8 \mydigitsclass
\XeTeXcharclass `\9 \mydigitsclass

% def interchartokes

\XeTeXinterchartoks \LetterClass \mydigitsclass = {\begingroup\huge}
\XeTeXinterchartoks \mydigitsclass \LetterClass  = {\endgroup}

\XeTeXinterchartoks 0 \mydigitsclass = {\begingroup\color{green}}
\XeTeXinterchartoks \mydigitsclass 0  = {\endgroup}
\XeTeXinterchartoks 4095 \mydigitsclass = {\begingroup\color{red}}
\XeTeXinterchartoks \mydigitsclass 4095  = {\endgroup}


\lstset{
%    basicstyle = \colordigits,
    identifierstyle = \ttfamily\color{blue},
    keywordstyle = \ttfamily\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]{?},
}


\begin{document}
\XeTeXinterchartokenstate=0
\section{Test}
\XeTeXinterchartokenstate=1
abc 012 345 678 9 xyz


[The lstlisting environment adds "\textbackslash glue 0 plus 1fil minus 1fil" betweeen every token]

%https://tex.stackexchange.com/questions/281566/xetex-special-xetexcharclass-needed-for-null-glues/321664#321664
\XeTeXinterchartokenstate=0
\subsection{Inline}
\XeTeXinterchartokenstate=1
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)

\XeTeXinterchartokenstate=0
\subsection{Verbatim}
\XeTeXinterchartokenstate=1
\begin{verbatim}
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)
\end{verbatim}

\XeTeXinterchartokenstate=0
\subsection{Listing}
\XeTeXinterchartokenstate=1
\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}

\XeTeXinterchartokenstate=0
\section{Processing}
\XeTeXinterchartokenstate=1

\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}

verwandte Informationen