Listados: colorear números constantemente

Listados: colorear números constantemente

he estado usandoesta soluciónencontrado aquí en LaTeX Stack Exchange para colorear números en extractos de mi código:

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

Funciona bastante bien pero lamentablemente también tiene algunos problemas. Adjuntando esto arriba de mi ejemplo mínimo a continuación...:

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

...da como resultado:

ingrese la descripción de la imagen aquí

Los resultados no son malos y es particularmente bueno que esa solución maneje números en nombres de variables independientemente de si van seguidos de un subrayado o no (por ejemplo, var_2y var3). Pero el ejemplo anterior también muestra un par de problemas que no he podido solucionar:

  • Es posible ver que los números precedidos por a (o inmediatamente seguidos por )no están coloreados, por ejemplo range(3),size(300, 300)
  • los números que tocan signos como comas, dos puntos o punto y coma tampoco están coloreados; por ejemplo, el número del medio en background(0, 200, 0);, o los números en x = x + 0.1;oif var_2 > 1.23:
  • por otro lado, tener espacios alrededor da como resultado un resaltado adecuado, por ejemplo y = 0 ;(con espacio antes del punto y coma) o el número 1000enelif (var3 <= 1000 and var_2 is None):

¿Alguien podría ayudarme a modificar este fragmento para que los números se resalten consistentemente en estas situaciones sin estar resaltados en los nombres de variables y funciones?

Editar: en pocas palabras, me gustaría que se resaltaran los números si:

  • vienen después de cualquiera de los siguientes caracteres: , ., (, {, [,:
  • vienen antes de cualquiera de los siguientes caracteres: , ., ), }, ], :,;
  • pero definitivamente no incluirlo _en ninguno de los grupos, ya que eso podría romper la convención de nomenclatura de variables en muchos fragmentos de código (aunque Python acepta _números para ayudar a la identificación visual de grupos de 10^3, como en x = 1_000_000.

Actualmente, el código anterior solo identifica correctamente y .de todos los caracteres mencionados en los puntos anteriores.

Editar: desafortunadamente mintedno es una opción para mí, no combina bien con mi archivo de tesis.

Respuesta1

Considere usar mintedy personalizar las hojas de estilo.

ingrese la descripción de la imagen aquí

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

Respuesta2

Si entendí la pregunta, desea colorear todos los dígitos a menos que estén en palabras clave o identificadores. Puede hacer esto utilizando como fuente base una fuente que tenga dígitos coloreados y una fuente diferente para las palabras clave. Con lualatex esto es posible:

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

ingrese la descripción de la imagen aquí

Respuesta3

Para completar, una solución que no es Lua y utiliza interchartoks en xetex falla en el último momento porque lstlistings inserta pegamento entre los tokens. Los interchartoks consideran que el pegamento es un marcador de límites entre palabras.

Así que no es una respuesta (sin reescribir el paquete (¿usando ensambladores de ancho cero?), incluso si el escenario de pdflatex fuera superable.

Para ilustrar las transiciones entre clases de tokens:

el rojo es la transición entre el límite de la palabra y el dígito.

pegamento de listado

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}

información relacionada