Main menu:

Site search

Categories

septembre 2009
L M M J V S D
 123456
78910111213
14151617181920
21222324252627
282930  

Archive

Colorisation syntaxique avec pygments

Elle consiste à donner à chaque élément d’un texte un style particulier. En programmation, cela consiste à donner une couleur et un style (gras, italique…) à chaque élément constituant le code source. La colorisation syntaxique rend le code plus lisible donc plus facilement compréhensible.

La plupart des éditeurs de texte le font automatiquement mais si vous devez formater le texte vous même pour l’afficher dans un wiki, une page web ou un blog, c’est une autre paire de manche.
J’utilise par exemple deux scripts qui me permettent de faire la colorisation syntaxique de code C++ ou python et me donnent un fichier html que je peux inclure dans une page web. Le problème est que si je veux documenter du code PHP, javascript ou des requètes SQL et faire la colorisation syntaxique moi-même, il faudra que je réécrive un nouveau script pour chaque langage. Heureusement pour moi, j’ai découvert une application écrite en python et trouvable sur http://pygments.org.

Pygments permet la colorisation syntaxique d’un très grand nombre de langages dont par exemple bash, C,C++, C#, Lisp, Lua, Delphi, PHP, Perl Python, Ruby, Tcl, Java, Javascript … Et je n’ai mis ici que les langages les plus connus. Il ye en a plein d’autres. Pygments permet aussi la colorisation syntaxique de fichiers de configuration, ini, css, html, sql, xml, latex,… De surcroît, on peut choisir le format de sortie: html, latex, rtf…

Pygments peut être utilisé en ligne de commande ou en tant que librairie dans un projet.

Pour l’installer, downloadez le fichier Pygments-1.1.tar.gz depuis http://pypi.python.org/pypi/Pygments
Ensuite tapez les commandes suivantes dans un terminal pour extraire de l’archive les fichiers et exécuter le fichier d’installation.

$ tar -zxvf Pygments-1.1.tar.gz
$ cd Pygments-1.1
$ sudo python setup.py install

Exemple d’utilisation en ligne de commande pour générer du code html:

Disons que nous voulons coloriser le bout de code python suivant:

# Returns the factorial of a number
def factorial(x):
    if x < 0:
        raise ValueError("Factorial is only defined for x >= 0")
    y = 1
    while x >= 2:
        y *= x
        x -= 1
    return y

Ce bout de code, plaçons-le dans un fichier appelé test.py et tapons la ligne suivante dans notre terminal:

$ pygmentize -f html -l python -o test.html test.py

-l choix du lexer, c’est à dire le module chargé de parser le code donné dans le langage spécifié et le séparer en ses composants (keywords, comments, chaînes de caractères…)

-f choix du formateur, c’est à dire le format sous lequel vous désirez le fichier de sortie (html, latex, rtf…).

Le résultat obtenu est le fichier html test.html ci-dessous:

<div class="highlight"><pre><span class="c"># Returns the factorial of a number</span>
<span class="k">def</span> <span class="nf">factorial</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">x</span> <span class="o"><</span> <span class="mf">0</span><span class="p">:</span>
        <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s">"Factorial is only defined for x >= 0"</span><span class="p">)</span>
    <span class="n">y</span> <span class="o">=</span> <span class="mf">1</span>
    <span class="k">while</span> <span class="n">x</span> <span class="o">>=</span> <span class="mf">2</span><span class="p">:</span>
        <span class="n">y</span> <span class="o">*=</span> <span class="n">x</span>
        <span class="n">x</span> <span class="o">-=</span> <span class="mf">1</span>
    <span class="k">return</span> <span class="n">y</span>
</pre></div>

Vous remarquerez que chaque constituant du code a son propre style avec sa classe propre. A vous de déterminer ce style en donnant au navigateur une feuille de style (css) compatible. Vous pouvez vous aider en utilisant la commande suivante qui liste les différents styles utilisés sous forme de css:

$ pygmentize -f html -S colorful 

.hll { background-color: #ffffcc }
.c { color: #808080 } /* Comment */
.err { color: #F00000; background-color: #F0A0A0 } /* Error */
.k { color: #008000; font-weight: bold } /* Keyword */
.o { color: #303030 } /* Operator */
.cm { color: #808080 } /* Comment.Multiline */
.cp { color: #507090 } /* Comment.Preproc */
.c1 { color: #808080 } /* Comment.Single */
.cs { color: #cc0000; font-weight: bold } /* Comment.Special */
.gd { color: #A00000 } /* Generic.Deleted */
.ge { font-style: italic } /* Generic.Emph */
.gr { color: #FF0000 } /* Generic.Error */
.gh { color: #000080; font-weight: bold } /* Generic.Heading */
.gi { color: #00A000 } /* Generic.Inserted */
.go { color: #808080 } /* Generic.Output */
.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
.gs { font-weight: bold } /* Generic.Strong */
.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.gt { color: #0040D0 } /* Generic.Traceback */
.kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.kp { color: #003080; font-weight: bold } /* Keyword.Pseudo */
.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.kt { color: #303090; font-weight: bold } /* Keyword.Type */
.m { color: #6000E0; font-weight: bold } /* Literal.Number */
.s { background-color: #fff0f0 } /* Literal.String */
.na { color: #0000C0 } /* Name.Attribute */
.nb { color: #007020 } /* Name.Builtin */
.nc { color: #B00060; font-weight: bold } /* Name.Class */
.no { color: #003060; font-weight: bold } /* Name.Constant */
.nd { color: #505050; font-weight: bold } /* Name.Decorator */
.ni { color: #800000; font-weight: bold } /* Name.Entity */
.ne { color: #F00000; font-weight: bold } /* Name.Exception */
.nf { color: #0060B0; font-weight: bold } /* Name.Function */
.nl { color: #907000; font-weight: bold } /* Name.Label */
.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
.nt { color: #007000 } /* Name.Tag */
.nv { color: #906030 } /* Name.Variable */
.ow { color: #000000; font-weight: bold } /* Operator.Word */
.w { color: #bbbbbb } /* Text.Whitespace */
.mf { color: #6000E0; font-weight: bold } /* Literal.Number.Float */
.mh { color: #005080; font-weight: bold } /* Literal.Number.Hex */
.mi { color: #0000D0; font-weight: bold } /* Literal.Number.Integer */
.mo { color: #4000E0; font-weight: bold } /* Literal.Number.Oct */
.sb { background-color: #fff0f0 } /* Literal.String.Backtick */
.sc { color: #0040D0 } /* Literal.String.Char */
.sd { color: #D04020 } /* Literal.String.Doc */
.s2 { background-color: #fff0f0 } $ pygmentize -O full -o test.html test.py/* Literal.String.Double */
.se { color: #606060; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */
.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */
.si { background-color: #e0e0e0 } /* Literal.String.Interpol */
.sx { color: #D02000; background-color: #fff0f0 } /* Literal.String.Other */
.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */
.s1 { background-color: #fff0f0 } /* Literal.String.Single */
.ss { color: #A06000 } /* Literal.String.Symbol */
.bp { color: #007020 } /* Name.Builtin.Pseudo */
.vc { color: #306090 } /* Name.Variable.Class */
.vg { color: #d07000; font-weight: bold } /* Name.Variable.Global */
.vi { color: #3030B0 } /* Name.Variable.Instance */
.il { color: #0000D0; font-weight: bold } /* Literal.Number.Integer.Long */

Il vous est aussi possible d’ajouter des numéros de lignes. Ceci crée en fait un tableau html dont la première colonne est le numéro de la ligne:

$ pygmentize -f html -l python -O style=colorful,linenos=1 -o test.html test.py

Si vous préférez un fichier html complet représentant une page complète avec entête et style utilisable directement avec le navigateur:

$ pygmentize -O full -o test.html test.py

Le résultat est ci-dessous. Notez l’entête, les CSS inclus dans le fichier et finalement votre code source formaté:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">

<html>
<head>
  <title></title>
  <meta http-equiv="content-type" content="text/html; charset=latin1">
  <style type="text/css">
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
pre { line-height: 125%; }
body .hll { background-color: #ffffcc }
body  { background: #f8f8f8; }
body .c { color: #408080; font-style: italic } /* Comment */
body .err { border: 1px solid #FF0000 } /* Error */
body .k { color: #008000; font-weight: bold } /* Keyword */
body .o { color: #666666 } /* Operator */
body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
body .cp { color: #BC7A00 } /* Comment.Preproc */
body .c1 { color: #408080; font-style: italic } /* Comment.Single */
body .cs { color: #408080; font-style: italic } /* Comment.Special */
body .gd { color: #A00000 } /* Generic.Deleted */
body .ge { font-style: italic } /* Generic.Emph */
body .gr { color: #FF0000 } /* Generic.Error */
body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
body .gi { color: #00A000 } /* Generic.Inserted */
body .go { color: #808080 } /* Generic.Output */
body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
body .gs { font-weight: bold } /* Generic.Strong */
body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
body .gt { color: #0040D0 } /* Generic.Traceback */
body .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
body .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
body .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
body .kp { color: #008000 } /* Keyword.Pseudo */
body .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
body .kt { color: #B00040 } /* Keyword.Type */
body .m { color: #666666 } /* Literal.Number */
body .s { color: #BA2121 } /* Literal.String */
body .na { color: #7D9029 } /* Name.Attribute */
body .nb { color: #008000 } /* Name.Builtin */
body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
body .no { color: #880000 } /* Name.Constant */
body .nd { color: #AA22FF } /* Name.Decorator */
body .ni { color: #999999; font-weight: bold } /* Name.Entity */
body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
body .nf { color: #0000FF } /* Name.Function */
body .nl { color: #A0A000 } /* Name.Label */
body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
body .nt { color: #008000; font-weight: bold } /* Name.Tag */
body .nv { color: #19177C } /* Name.Variable */
body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
body .w { color: #bbbbbb } /* Text.Whitespace */
body .mf { color: #666666 } /* Literal.Number.Float */
body .mh { color: #666666 } /* Literal.Number.Hex */
body .mi { color: #666666 } /* Literal.Number.Integer */
body .mo { color: #666666 } /* Literal.Number.Oct */
body .sb { color: #BA2121 } /* Literal.String.Backtick */
body .sc { color: #BA2121 } /* Literal.String.Char */
body .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
body .s2 { color: #BA2121 } /* Literal.String.Double */
body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
body .sh { color: #BA2121 } /* Literal.String.Heredoc */
body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
body .sx { Colorisation syntaxique avec pygmentscolor: #008000 } /* Literal.String.Other */
body .sr { color: #BB6688 } /* Literal.String.Regex */
body .s1 { color: #BA2121 } /* Literal.String.Single */
body .ss { color: #19177C } /* Literal.String.Symbol */
body .bp { color: #008000 } /* Name.Builtin.Pseudo */
body .vc { color: #19177C } /* Name.Variable.Class */
body .vg { color: #19177C } /* Name.Variable.Global */
body .vi { color: #19177C } /* Name.Variable.Instance */
body .il { color: #666666 } /* Literal.Number.Integer.Long */

  </style>
</head>
<body>
<h2></h2>

<div class="highlight"><pre><span class="c"># Returns the factorial of a number</span>
<span class="k">def</span> <span class="nf">factorial</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">x</span> <span class="o"><</span> <span class="mf">0</span><span class="p">:</span>
        <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s">"Factorial is only defined for x >= 0"</span><span class="p">)</span>
    <span class="n">y</span> <span class="o">=</span> <span class="mf">1</span>
    <span class="k">while</span> <span class="n">x</span> <span class="o">>=</span> <span class="mf">2</span><span class="p">:</span>
        <span class="n">y</span> <span class="o">*=</span> <span class="n">x</span>
        <span class="n">x</span> <span class="o">-=</span> <span class="mf">1</span>
    <span class="k">return</span> <span class="n">y</span>
</pre></div>
</body>
</html>

Et vous pouvez voir ici le résultat de tous vos efforts.

Comments

Comment from Maxime
Time: 28 novembre 2010, 13 h 20 min

Bonjour et merci pour ce tutoriel.

Je suis en train d’essayer pygments pour colorer du xml contenu dans du LaTeX.

J’ai tapé en console : pygmentize -f latex -O full source.tex > destination.tex

Cepandent j’ai une erreur d’encodage :
*** Error while highlighting:
UnicodeDecodeError: ‘utf8’ codec can’t decode bytes in position 388-390: invalid data
(file « /usr/lib/python2.6/encodings/utf_8.py », line 16, in decode)

Que puis-je faire ?

Merci,

Cordialement,

Maxime.

Comment from admin
Time: 28 novembre 2010, 13 h 36 min

Bonjour,
D’après le message, il y a dans ton fichier source, un caractère invalide en position 388-390. Essaie de trouver le caractère qui pose problème et vérifier s’il est réellement invalide. Sinon, autre possibilité un bug dans la librairie python.