Listados de código

Elegir entre listings y minted para componer código fuente en LaTeX no es una cuestión de gusto cromático. listings logra su resaltado de sintaxis solo con macros de TeX, y todo lo que sabe de un lenguaje cabe en una lista escrita a mano de qué palabras cuentan como palabras clave. minted entrega ese trabajo en bloque a Pygments, un analizador léxico escrito en Python: el coloreado pasa así a otra categoría, pero el paquete debe salir de LaTeX para conseguirlo. Calidad o portabilidad: ese fue el trato durante años, hasta que minted 3 reescribió sus términos. Esta página ordena ambos paquetes desde la situación actual.

La diferencia entre listings y minted

La diferencia se reduce a una sola cosa: quién hace el resaltado. listings se basta a sí mismo con macros de LaTeX puras, de modo que \usepackage{listings} funciona sin más, tanto en Overleaf como en un aula de informática cuya configuración no se puede tocar. minted llama a un programa externo y recoge su análisis, lo que gana en precisión pero convierte la disponibilidad de ese programa en una condición previa. Elegir entre ambos es, en el fondo, predecir dónde se compilará finalmente el documento.

Esa «lista escrita a mano» no es una metáfora. Las definiciones de lenguaje de listings viven en tres archivos, de lstlang1.sty a lstlang3.sty, y su contenido es una sucesión de entradas del tipo \lst@definelanguage{ACSL}[90]{Fortran}{morekeywords={algorithm,cinterval,...}}: palabras clave separadas por comas. Contando la copia que acompaña a TeX Live 2024 salen unos 95 lenguajes. Pygments, en cambio, es una biblioteca de análisis léxico independiente desarrollada desde 2006 por Georg Brandl y otros; en Pygments 2.19, pygmentize -L lexers enumera 597 analizadores. Más que la diferencia de cifras importa la de principio: de un lado una lista de palabras, del otro un analizador que trocea un flujo en unidades léxicas según una gramática.

listingsminted
highlightingaproximación por lista de palabras claveanálisis léxico real mediante Pygments
external toolsninguna (macros de LaTeX puras)Pygments; minted 3 incluye latexminted
-shell-escapeno hace faltaobligatorio en minted 2; innecesario en minted 3 desde TeX Live 2025
languagesunos 95 (versión de TeX Live 2024)597 analizadores (Pygments 2.19)
UTF-8error fatal con pdfLaTeXlos caracteres se pierden con pdfLaTeX (aun así se genera el PDF)

Fundamentos de listings: el entorno lstlisting y \lstinputlisting

Solo hay tres puntos de entrada. Para escribir código directamente en el documento, el entorno lstlisting; para incluir un archivo externo tal cual, \lstinputlisting{sample.py}; para insertar un fragmento corto en el texto corriente, \lstinline. El aspecto se fija después una sola vez en el preámbulo con \lstset{...}, en lugar de repetirlo en cada llamada. listings ofrece bastante más de cien opciones, pero la docena larga que aparece en el ejemplo siguiente basta para el uso habitual.

document.tex
\usepackage{listings}
\usepackage{xcolor}   % needed for the \color{...} styles below

\lstset{
  language=Python,
  basicstyle=\ttfamily\small,      % base font for the code
  keywordstyle=\color{blue}\bfseries,
  commentstyle=\color{teal}\itshape,
  stringstyle=\color{red!60!black},
  numbers=left,                    % line numbers in the left margin
  numberstyle=\tiny\color{gray},
  frame=single,                    % draw a thin frame around the block
  breaklines=true,                 % wrap lines that are too long
  showstringspaces=false,
  tabsize=2,
}

\begin{lstlisting}[caption={Factorial, computed recursively}, label=lst:fact]
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)
\end{lstlisting}

% pull in lines 37-45 of an external file, without line numbers
\lstinputlisting[language=C, firstline=37, lastline=45, numbers=none]{sample.c}

Las claves de \lstset se recuerdan mejor agrupadas por función. La tipografía corresponde a basicstyle (\ttfamily\small es lo habitual). El color con valor semántico corresponde a keywordstyle, commentstyle y stringstyle. La decoración del entorno corresponde a numbers=left (números de línea a la izquierda, formateados por numberstyle) y a frame=single (un borde). breaklines=true pliega las líneas que rebasan la caja; olvidarlo hace que el código atraviese el margen derecho, el tropiezo más frecuente en la práctica. Con caption= y label=, el bloque se convierte en un listado numerado al mismo nivel que una figura o una tabla, referenciable con \ref{lst:fact}.

Los ajustes del preámbulo pueden sobrescribirse en cada bloque dentro de [ ]. Escribir \begin{lstlisting}[language=C, numbers=none] deja solo ese bloque en C y sin números de línea. Con archivos externos entran además firstline= y lastline=: \lstinputlisting[firstline=37, lastline=45]{sample.c} extrae exactamente las líneas necesarias, algo que rinde en la práctica, porque nada se copia al documento y corregir el archivo original actualiza el texto por sí solo. El código en línea sigue el uso de \verb: se elige cualquier carácter como delimitador, por ejemplo \lstinline|while (i < n)|.

Por qué el CJK en un listado se detiene con Invalid UTF-8 byte sequence

No es un problema del paquete sino un problema del motor. Al compilar con pdfLaTeX, un ideograma o una sílaba hangul dentro del código se convierte, bajo listings, en el error fatal ! LaTeX Error: Invalid UTF-8 byte sequence, y no se produce PDF alguno. Cambiar a minted no lo arregla: minted informa ! LaTeX Error: Unicode character y luego construye en silencio un PDF al que le falta ese carácter. Ambos síntomas tienen la misma raíz: los caracteres multibyte no caben en el supuesto de pdfTeX de que un byte es un carácter.

El consejo muy difundido de cargar listingsutf8 no sirve para el CJK. El README del paquete lo dice con todas las letras: el apaño solo vale si existe una codificación de un byte a la que convertir el archivo, y solo actúa sobre \lstinputlisting. Las letras acentuadas de las lenguas europeas pueden bajarse a latin1, pero ninguna codificación de un byte alberga ideogramas, kana o hangul: no hay destino al que convertir. Al ejecutar de veras \lstinputlisting[inputencoding=utf8/latin1]{sample.py}, el error desaparece, sí, y con él desaparecen de la salida los propios caracteres. El modo de fallo es especialmente traicionero porque una compilación silenciosa parece un éxito.

La solución de verdad es cambiar de motor. Componiendo con XeLaTeX o LuaLaTeX —ambos tratan su entrada como Unicode desde el principio—, tanto listings como minted admiten sin más el código con comentarios en japonés. Solo queda que la fuente monoespaciada contenga esos caracteres, algo que se elige con \setmonofont de fontspec. Una advertencia aquí: una fuente suele cubrir únicamente su propia lengua. Componer chino simplificado o hangul con una fuente japonesa produce una hilera de avisos Missing character, y esos caracteres desaparecen. Para código con varias escrituras conviene, pues, elegir una tipografía que las cubra todas. En cambio, si solo hay que dar paso a un puñado de letras acentuadas europeas, la vía clásica sigue sirviendo bajo pdfLaTeX: enseñarlas una a una, por ejemplo con \lstset{literate={é}{{\'e}}1}.

document.tex
% Compile this with xelatex or lualatex, not pdflatex.
% A monospaced face that actually covers the script you use.
\usepackage{fontspec}
\setmonofont{Noto Sans Mono CJK JP}

\usepackage{listings}
\lstset{basicstyle=\ttfamily\small}

\begin{lstlisting}[language=Python]
def factorial(n):
    # a comment written in your own language survives here
    return 1 if n <= 1 else n * factorial(n - 1)
\end{lstlisting}

Fundamentos de minted: \begin{minted}{python} e \inputminted

La forma se parece mucho a la de listings, con una diferencia: el lenguaje es un argumento obligatorio. Su nombre va en el argumento del entorno, como en \begin{minted}{python}; para un archivo externo es \inputminted{python}{sample.py}, y para un fragmento en el texto \mintinline{python}{print("hi")}. El lenguaje no puede omitirse porque hay que indicarle a Pygments un único analizador antes de que el análisis pueda siquiera empezar. Aquí no existe el «se fija una vez en el preámbulo y luego se omite» de listings.

document.tex
\usepackage{minted}

\usemintedstyle{monokai}          % one colour theme for the whole document
% \setminted{style=monokai, linenos, fontsize=\small}  % broader defaults

\begin{minted}[linenos, bgcolor=black!90, fontsize=\small]{python}
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)
\end{minted}

\mint{python}|print("Hello!")|            % one line, no environment
\mintinline{python}{print("Hello!")}     % inside running text
\inputminted[linenos]{python}{sample.py} % a whole external file

% "text" turns highlighting off without leaving minted
\begin{minted}{text}
plain output, no keywords coloured
\end{minted}

Las opciones van en [ ] justo después del nombre del entorno, como pares key=value. Las más comunes son linenos para los números de línea, style= para elegir un juego de colores de Pygments, bgcolor= para un fondo y fontsize=. Se aplica un juego a todo el documento con \usemintedstyle{monokai}, o se fija un lote de valores por defecto con \setminted{style=monokai, linenos}. Para un lenguaje que Pygments desconoce, o para un bloque que se quiere dejar liso a propósito, se indica text como lenguaje. Una advertencia: \mint no es el comando en línea; solo ahorra escribir un entorno alrededor de una única línea de código. Para fundir código en el texto corriente, úsese siempre \mintinline.

Por qué minted exige -shell-escape y desde cuándo prescinde de él

minted lanza un programa externo en plena composición y por eso exige shell escape, es decir, el permiso para que LaTeX ejecute órdenes externas. Sin él la compilación se detiene en ! Package minted Error: You must invoke LaTeX with the -shell-escape flag. Hay que compilar con -shell-escape bajo pdfLaTeX, o con -enable-write18 bajo MiKTeX.

terminal
pdflatex -shell-escape document.tex
xelatex  -shell-escape document.tex

# MiKTeX uses the older spelling
pdflatex -enable-write18 document.tex

Esto es justamente lo que cambió minted 3. Antes había que instalar uno mismo Python y Pygments y luego abrir un shell escape sin restricciones, y en eso consistía realmente la disyuntiva entre calidad y portabilidad con la que abría esta página. minted 3 reúne la parte de Python en un ejecutable propio llamado latexminted y lo distribuye dentro de las distribuciones de TeX como un wheel de Python. Su autor, Geoffrey M. Poore, lo describe como diseñado para ser compatible con los requisitos de seguridad de LaTeX para ejecutables de shell escape restringido. El resultado: en TeX Live 2025, latexminted figura en la lista de permitidos del shell escape restringido y se puede compilar sin -shell-escape en absoluto, y también desaparece la instalación aparte de Pygments.

Ahora bien, si la instalación local es más antigua, la historia cambia. TeX Live 2024 incluye minted 2.9, de diciembre de 2023, y esa versión sigue negándose a funcionar sin -shell-escape. Saber en qué lado se está resulta sencillo: o aparece el error de arriba o no. Además, habilitar shell escape equivale a conceder a ese documento permiso para ejecutar órdenes externas arbitrarias. Nunca se debe ejecutar un .tex de procedencia desconocida con -shell-escape. Por esa misma razón los sistemas de envío de congresos y editoriales lo prohíben a veces por completo, así que conviene comprobar una vez, antes de enviar, que el documento compila también sin esa opción.

Llamar a un proceso externo hace que minted compile más despacio que listings. Lo que lo compensa es la caché: minted guarda cada fragmento ya resaltado en un directorio de trabajo y no vuelve a llamar a Pygments mientras el código no cambie. Componer document.tex bajo TeX Live 2024 crea aquí un directorio _minted-document/ con archivos .pygtex nombrados según una huella del fragmento de código. A ese mecanismo se deben las compilaciones posteriores, notablemente más rápidas. La caché se desactiva con cache=false; y cuando los colores parecen equivocados, o un cambio de esquema se niega a aparecer, borrar el directorio entero es el remedio más rápido. Conviene mantenerlo fuera del control de versiones.

Entonces, cuál conviene usar

En realidad solo hay un eje: dónde se compilará este documento. Si es únicamente en la máquina propia, o en un entorno cuidado como Overleaf, el coloreado de minted es claramente mejor, y con minted 3 sobre TeX Live 2025 o posterior ya no se paga el precio de antes. Si, en cambio, no se controlan ni las máquinas de los coautores ni la cadena de procesado del otro extremo, el hecho de que listings funcione sin más vale más que unos colores exactos. En caso de duda, recórrase la lista siguiente.

  • No se pueden instalar herramientas externas ni usar shell escape → listings, sin discusión. Todo se reduce a \usepackage{listings}.
  • La precisión del resaltado y la cobertura de lenguajes son lo primero → minted. El coloreado mediante Pygments juega en otra categoría.
  • El código contiene japonés, chino o coreano → hay que cambiar de motor, no de paquete. Componer con XeLaTeX o LuaLaTeX y apuntar \setmonofont a una tipografía monoespaciada que cubra esa escritura.
  • Ni colores ni números de línea, solo el texto tal como se escribióverbatim o fancyvrb son más ligeros.
  • Se trata de escribir pseudocódigo y no código ejecutablealgorithm2e y algpseudocode son las herramientas hechas para eso.