Listings de code

Choisir entre listings et minted pour composer du code source en LaTeX n'est pas une affaire de goût en matière de couleurs. listings réalise sa coloration syntaxique avec les seules macros de TeX, et tout ce qu'il sait d'un langage tient dans une liste écrite à la main des mots qui comptent comme mots-clés. minted confie ce travail en bloc à Pygments, un analyseur lexical écrit en Python : la coloration change alors de catégorie, mais le package doit sortir de LaTeX pour y parvenir. La qualité ou la portabilité : tel fut le marché pendant des années, jusqu'à ce que minted 3 en réécrive les termes. Cette page fait le point sur les deux packages depuis la situation actuelle.

La différence entre listings et minted

La différence tient à un seul point : qui effectue la coloration. listings se suffit à lui-même en macros LaTeX pures, si bien que \usepackage{listings} fonctionne tel quel, sur Overleaf comme dans une salle informatique dont la configuration est intouchable. minted appelle un programme externe et en récupère l'analyse, ce qui l'emporte sur la précision mais fait de la disponibilité de ce programme une condition préalable. Choisir entre les deux revient au fond à prédire où le document sera compilé en dernier ressort.

Cette « liste écrite à la main » n'est pas une métaphore. Les définitions de langages de listings tiennent dans trois fichiers, de lstlang1.sty à lstlang3.sty, et leur contenu est une suite d'entrées du genre \lst@definelanguage{ACSL}[90]{Fortran}{morekeywords={algorithm,cinterval,...}} : des mots-clés séparés par des virgules. Dans la version livrée avec TeX Live 2024, cela fait environ 95 langages. Pygments, à l'inverse, est une bibliothèque d'analyse lexicale indépendante développée depuis 2006 par Georg Brandl et d'autres ; sur Pygments 2.19, pygmentize -L lexers en énumère 597. L'écart des chiffres compte moins que l'écart de principe : d'un côté une liste de mots, de l'autre un analyseur qui découpe un flux en unités lexicales selon une grammaire.

listingsminted
highlightingapproximation par liste de mots-clésvéritable analyse lexicale par Pygments
external toolsaucun (macros LaTeX pures)Pygments ; minted 3 fournit latexminted
-shell-escapeinutilerequis par minted 2 ; inutile pour minted 3 à partir de TeX Live 2025
languagesenviron 95 (version de TeX Live 2024)597 analyseurs (Pygments 2.19)
UTF-8erreur fatale sous pdfLaTeXcaractères perdus sous pdfLaTeX (un PDF est tout de même produit)

Bases de listings : l'environnement lstlisting et \lstinputlisting

Il n'y a que trois points d'entrée. Pour écrire du code directement dans le document, l'environnement lstlisting ; pour inclure un fichier externe tel quel, \lstinputlisting{sample.py} ; pour glisser un court fragment dans le texte courant, \lstinline. L'apparence se règle ensuite une seule fois dans le préambule avec \lstset{...}, plutôt qu'à chaque appel. listings propose bien plus de cent options, mais la douzaine qui figure dans l'exemple ci-dessous suffit à l'usage courant.

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}

Les clés de \lstset se retiennent mieux regroupées par fonction. La police relève de basicstyle (\ttfamily\small est le choix courant). La couleur porteuse de sens relève de keywordstyle, commentstyle et stringstyle. La décoration autour du bloc relève de numbers=left (numéros de ligne à gauche, mis en forme par numberstyle) et de frame=single (un cadre). breaklines=true replie les lignes qui débordent de la justification ; l'oublier, c'est voir le code traverser la marge de droite — le piège le plus fréquent en pratique. Avec caption= et label=, le bloc devient un listing numéroté au même titre qu'une figure ou un tableau, référençable par \ref{lst:fact}.

Les réglages du préambule peuvent être remplacés bloc par bloc dans [ ]. \begin{lstlisting}[language=C, numbers=none] ne rend que ce bloc-là en C et sans numéros de ligne. Pour les fichiers externes s'ajoutent firstline= et lastline= : \lstinputlisting[firstline=37, lastline=45]{sample.c} extrait exactement les lignes utiles — un vrai gain en pratique, puisque rien n'est recopié dans le document et qu'une correction du fichier d'origine se répercute automatiquement. Le code en ligne suit l'usage de \verb : on choisit n'importe quel caractère comme délimiteur, par exemple \lstinline|while (i < n)|.

Pourquoi du CJK dans un listing s'arrête sur Invalid UTF-8 byte sequence

Ce n'est pas un problème de package mais un problème de moteur. À la compilation avec pdfLaTeX, un idéogramme ou une syllabe hangul placé dans le code devient, sous listings, l'erreur fatale ! LaTeX Error: Invalid UTF-8 byte sequence, et aucun PDF n'est produit. Passer à minted ne règle rien : minted signale ! LaTeX Error: Unicode character puis fabrique sans bruit un PDF où le caractère manque. Les deux symptômes ont la même racine : les caractères multioctets n'entrent pas dans le postulat de pdfTeX selon lequel un octet vaut un caractère.

Le conseil très répandu de charger listingsutf8 ne résout rien pour le CJK. Le README du package en donne explicitement la raison : le contournement ne vaut que s'il existe un encodage sur un octet vers lequel convertir le fichier, et il n'agit que sur \lstinputlisting. Les lettres accentuées des langues européennes peuvent redescendre vers latin1, mais aucun encodage sur un octet ne contient les idéogrammes, les kana ou le hangul : il n'y a pas de cible de conversion. En exécutant réellement \lstinputlisting[inputencoding=utf8/latin1]{sample.py}, l'erreur disparaît bel et bien — et les caractères eux-mêmes disparaissent de la sortie. Le mode de défaillance est d'autant plus perfide qu'une compilation silencieuse ressemble à une réussite.

La vraie solution consiste à changer de moteur. En composant avec XeLaTeX ou LuaLaTeX — l'un comme l'autre traitent leur entrée comme de l'Unicode dès le départ —, listings et minted acceptent tels quels des codes contenant des commentaires en japonais. Il ne reste plus qu'à disposer d'une police à chasse fixe contenant ces caractères, choisie avec \setmonofont de fontspec. Une mise en garde ici : une police ne couvre généralement que sa propre langue. Composer du chinois simplifié ou du hangul dans une police japonaise produit une série d'avertissements Missing character, et ces caractères-là disparaissent. Pour du code mêlant plusieurs écritures, choisissez donc une fonte qui les couvre toutes. En revanche, s'il ne s'agit que de faire passer quelques lettres accentuées européennes, la voie classique reste valable sous pdfLaTeX : les enseigner une à une, par exemple avec \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}

Bases de minted : \begin{minted}{python} et \inputminted

La structure ressemble beaucoup à celle de listings, à une différence près : le langage est un argument obligatoire. Son nom figure dans l'argument de l'environnement, comme dans \begin{minted}{python} ; pour un fichier externe, c'est \inputminted{python}{sample.py}, et pour un fragment dans le texte \mintinline{python}{print("hi")}. Le langage ne peut pas être omis, car il faut désigner à Pygments un et un seul analyseur avant que l'analyse puisse commencer. Le « on le fixe une fois dans le préambule et on l'omet ensuite » de listings n'existe pas ici.

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}

Les options se placent dans [ ] juste après le nom de l'environnement, sous forme de paires key=value. Les plus courantes sont linenos pour les numéros de ligne, style= pour choisir un jeu de couleurs Pygments, bgcolor= pour un fond et fontsize=. On applique un jeu à tout le document avec \usemintedstyle{monokai}, ou l'on fixe un lot de valeurs par défaut avec \setminted{style=monokai, linenos}. Pour un langage inconnu de Pygments, ou pour un bloc volontairement laissé nu, indiquez text comme langage. Un avertissement : \mint n'est pas la commande en ligne — elle évite seulement d'écrire un environnement autour d'une unique ligne de code. Pour fondre du code dans le texte courant, employez toujours \mintinline.

Pourquoi minted exige -shell-escape, et depuis quand il s'en passe

minted lance un programme externe en pleine composition et réclame donc le shell escape, c'est-à-dire l'autorisation pour LaTeX d'exécuter des commandes externes. Sans elle, la compilation s'arrête sur ! Package minted Error: You must invoke LaTeX with the -shell-escape flag. Il faut compiler avec -shell-escape sous pdfLaTeX, ou -enable-write18 sous MiKTeX.

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

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

C'est précisément ce que minted 3 a changé. Auparavant, il fallait installer soi-même Python et Pygments, puis ouvrir un shell escape sans restriction : voilà en quoi consistait réellement l'alternative qualité contre portabilité évoquée en ouverture. minted 3 regroupe la partie Python dans un exécutable dédié nommé latexminted et le distribue au sein des distributions TeX sous forme de wheel Python. Son auteur, Geoffrey M. Poore, le décrit comme conçu pour être compatible avec les exigences de sécurité de LaTeX applicables aux exécutables de shell escape restreint. Résultat : sous TeX Live 2025, latexminted figure sur la liste d'autorisation du shell escape restreint et la compilation se passe entièrement de -shell-escape ; l'installation séparée de Pygments disparaît elle aussi.

Si l'installation locale est plus ancienne, en revanche, l'histoire change. TeX Live 2024 livre minted 2.9, daté de décembre 2023, et cette version refuse toujours de fonctionner sans -shell-escape. Savoir de quel côté on se trouve est simple : ou bien l'erreur ci-dessus apparaît, ou bien non. Par ailleurs, activer le shell escape revient à accorder à ce document le droit d'exécuter des commandes externes arbitraires. Ne jamais lancer un fichier .tex d'origine inconnue avec -shell-escape. C'est pour la même raison que les systèmes de soumission de conférences et d'éditeurs l'interdisent parfois purement et simplement ; mieux vaut donc vérifier une fois, avant de soumettre, que le document se compile aussi sans cette option.

Faire appel à un processus externe rend minted plus lent à compiler que listings. Ce qui compense, c'est le cache : minted enregistre chaque fragment déjà coloré dans un répertoire de travail et ne rappelle Pygments que si le code change. Composer document.tex sous TeX Live 2024 crée ici un répertoire _minted-document/ contenant des fichiers .pygtex nommés d'après une empreinte du fragment de code. C'est à ce mécanisme que l'on doit des compilations suivantes nettement plus rapides. Le cache se désactive par cache=false ; et lorsque les couleurs semblent fausses, ou qu'un changement de jeu de couleurs refuse obstinément d'apparaître, supprimer le répertoire entier est le remède le plus rapide. À tenir hors du contrôle de version.

Lequel employer, en fin de compte

Il n'y a en réalité qu'un seul axe : où ce document sera-t-il compilé ? Si c'est uniquement sur sa propre machine, ou dans un environnement soigné comme Overleaf, la coloration de minted est nettement supérieure — et avec minted 3 sous TeX Live 2025 ou plus récent, elle ne se paie plus au prix fort d'autrefois. Si en revanche on ne maîtrise ni les machines des coauteurs ni la chaîne de traitement à l'arrivée, le fait que listings fonctionne sans histoires vaut mieux que des couleurs exactes. Dans le doute, dérouler la liste suivante.

  • Impossible d'installer des outils externes ou d'utiliser le shell escape → listings, sans hésiter. Tout tient dans \usepackage{listings}.
  • La précision de la coloration et la couverture des langages priment → minted. La coloration par Pygments est à part.
  • Le code contient du japonais, du chinois ou du coréen → changer de moteur, pas de package. Composer avec XeLaTeX ou LuaLaTeX et diriger \setmonofont vers une fonte à chasse fixe couvrant l'écriture concernée.
  • Ni couleurs ni numéros de ligne, seulement le texte tel qu'il a été saisiverbatim ou fancyvrb sont plus légers.
  • Il s'agit d'écrire du pseudocode et non du code exécutablealgorithm2e et algpseudocode sont les outils prévus pour cela.