Du joli code dans les articles

Ceci est une ARCHIVE, peut-être périmée. Vérifiez bien les compatibilités !

Ou comment avoir du code coloré dans spip grâce aux nouveaux points d’entrée dans inc_texte. une démo ici.

Quand on insère du code dans un article avec les balises <code>, on peut avoir une présentation différente en ajustant le style css. Pourtant, pour des gros morceaux de code, ça fait vite mal aux yeux.

screenshot
un screenshot de la version complexe. On y voit du code php, java et html coloré.

Voici une utilisation des points d’entrée dans inc_texte.php3 pour faire du « syntax highlighting » du code inséré dans les articles. Ainsi, comme dans tout bon éditeur de code, on peut voir les mots clefs, nom de fonctions etc... dans des couleurs différentes. On a ainsi une lecture plus rapide.

-  La version « simple » utilise une fonction de php existante pour colorer du code php compris entre des balises <ccode> et </ccode>.

-  La version plus complexe nécessite l’installation d’un module php externe disponible ici ou en version améliorée par mes petites mains ici qui permet de colorer n’importe quel langage [1].

les codes spécifiques se trouvent après l’explication.

Méthode

A partir de la version 1.7.1 de SPIP deux points d’entrée dans inc_texte.php3 sont fournis [2]. Ce fichier est responsable du traitement des raccourcis typographiques que les rédacteurs utilisent quand ils écrivent un article (gras, italique, balises code, etc...) :
-  avant_propre permet de traiter le texte avant que SPIP ne le traite lui même et donc d’intercepter des raccourcis qui seraient modifiés par SPIP autrement.
-  apres_propre permet de traiter le texte après tous les traitements de SPIP.

Vous pouvez donc implémenter, dans votre propre fichier ecrire/mes_options.php des pré/post-traitements pour des nouveaux raccourcis. Voir par exemple cet article, ou ce wiki et cette explication de Fil.

Bon, je vous ment un tout petit peu. Spip, avant d’appeler avant_propre, va échapper (les enlever du texte) certaines parties du texte qui pourraient être sensibles. par exemple le code entre balises <code>. Une fois tout le traitement typographique fait (et donc après apres_propre), Spip réinsère le texte échappé au bon endroit. On verra ce que cela implique dans quelques instants.

La méthode que l’on va utilisée est la même : échapper le code entre les balises <ccode> </ccode> pour le réinsérer tout à la fin avec les informations de couleurs.

Version Simple

Ici, je dit version simple, mais elle implique la même utilisation des points d’entrée dans inc_texte. La seule différence est la technique de coloration du code, qui implique une toute petite différence dans le code de ecrire/mes_options.php3.

Coloration

PHP fournie, avec la fonction highlight_string un moyen de colorer du code. Il y a deux conditions :
-  il doit s’agir de code PHP [3],
-  le code doit être compris entre les balises php standards : <?php et ?> .

prétraitement du texte

dans ecrire/mes_options.php3

function avant_colorer_code($texte) {
   global $code_a_colorer;


   $regexp_echap = "(<ccode>(([^<]|<[^/]|</[^c]|</c[^c]|</cc[^o]|</cco[^d]|</ccod[^e]|</ccode[^>])*)<\/ccode>)";

   $colo_code_ech=0;
   //on échappe tout ce qui se trouve entre <ccode> et </ccode>
   while (preg_match($regexp_echap, $texte, $regs)) {
      //on stoque le code à colorer pour plus tard.
      $code_a_colorer[$colo_code_ech] = $regs[1];
      $pos = strpos($texte, $regs[0]);
     //on le remplace par une balise <@@colorercode_X@@>
     $texte = substr($texte,0,$pos).
                 "<@@colorercode_$colo_code_ech@@>".
                 substr($texte,$pos+strlen($regs[0]));
      $colo_code_ech++;
   }
   return $texte;
}

function avant_propre($texte) {
   $new_texte = avant_colorer_code($texte);
   return $new_texte;
}

}

Ce code permet d’échapper le code qui se trouve entre nos deux balises. On remplace cette partie du texte par une balise spéciale [4] <@@colorercode_..> qui comprend un index dans le tableau dans lequel on stoque temporairement le code escamoté.

post traitement

dans ecrire/mes_options.php3

function apres_colorer_code($texte) {
   global $code_a_colorer;

   //on remplace toutes les balises qu'on a insérées par le code coloré.
   while (ereg('<@@colorercode_([0-9]+)@@>', $texte, $regs)) {
      //on récupère l'indexe du code dans le tableau.
      $lenum = $regs[1];
      $lecode = highlight_string("<?php".$code_a_colorer[$lenum]."?>",true);

      $pos = strpos($texte, $regs[0]);
      //on enlève les balises que highlight a ajouté
      $lecode = preg_replace("/(<code>|<\/code>)/","",$lecode);

      //on insère une nouvelle balise avec un style css.
      $texte = substr($texte, 0, $pos).
                    "<tt><div class='spip_ccode' dir='ltr'>". 
                    $lecode .
                    "</div></tt>". 
                   substr($texte, $pos + strlen($regs[0]));
   }
   //on corrige les balises d'échappement de spip qui ont été transformée par highlight.
   $find = "/&lt;@@SPIP_(SOURCEPROPRE|SOURCETYPO)([0-9]+)@@&gt;/";
   $replace = "<@@SPIP_\\1\\2@@>";
   $texte = preg_replace($find,$replace,$texte);

   return $texte;
}

function apres_propre($texte) {
   $new_texte = apres_colorer_code($texte);
   return $new_texte;
}

Cette fonction est donc appelée à la fin du traitement. On y remplace toutes les balises d’échappement (<@@colorercode_..>) par le code coloré.

On récupère l’index dans le tableau, puis on appelle la fonction highlight sur le code. Il faut ensuite lui faire un petit traitement :
-  on ajoute le code de formatage, qui met le code coloré dans un DIV de classe spip_ccode [5],
-  on enléve les balises code (html cette fois, pas spip) ajoutées par la fonction highlight,

La dernière étape nécessite quelques explication. Comme mentionné plus tôt, SPIP échappe un certain nombre de chose avant d’appeler avant_propre, et le remplace par des balises spéciales. Si dans notre code, spip a trouvé quelque chose à échappé (par exemple certain code html au milieu du code php), alors il le fera [6].

Mais les balises spéciales spip sont ensuite traitées par l’highlighter, ainsi les < et > sont remplacés par &lt; et &gt;. On doit donc les retransformer pour avoir des < et >.

Cela pose un deuxième problème, prenons un exemple : Si votre code php contient une balise d’image

$reg = "/<img src='IMG\/([^']+)'.+alt=(.+) title=([^>]+)>/";

,
alors spip va l’échapper :
$reg = "<@@SPIPSOURCEPROPREX@@>";
et le remettre en place après votre traitement du code php. Ainsi la chaine de caractéres ne sera pas colorée, mais prise comme une vraie balise html [7]. il faut donc mettre tout le code html qui se trouve dans votre code php entre balises spip <code>, sous peine d’avoir de mauvaises surprises. Ainsi notre exemple devra être écrit :
$reg = "/<code><img src='IMG\/([^']+)'.+alt=(.+) title=([^>]+)></code>/";

il faut ensuite faire attention au style. Insérez ce code [8] :

dans habillage.css par exemple

.spip_ccode .spip_code{

border: 0px;
padding: 0px;
margin-left: 0px;
background: transparent;

}

dans votre fichier css [9], pour annuler le style .spip_code à l’intérieur d’un bloc .spip_ccode.

Méthode « complexe »

Ici je décris l’installation du code Beautifier et donne un code adapté d’avant_propre et apres_propre. La technique utilisée dans ces deux méthode n’étant pas différente, je ne la redécris pas.

Beautifier

Je vous conseille d’utiliser l’adaptation du beautifier disponible ici [10].

Le beautifier est simple à installer :
- télécharger les fichiers de base,
- télécharger aussi les fichiers de formatages pour les langages qui vous intéressent.
- décompresser les packages où vous voulez dans votre repertoire spip (par exemple dans ecrire/)
- adapté le chemin dans le fichier Beautifier/Init.php
- adapté le chemin dans la fonction décrite ici.

On va donc appeler depuis spip la fonction du beautifier de cette façon :

dans ecrire/mes_options.php3

function my_highlight_string($texte, $file='php3') {
//ajuster le chemin ici
  $m8_path = '/users/mortimer/ecrire/beautifier';
  require_once "$m8_path/Beautifier/Init.php";
  $class_name = "HFile_$file";
  require_once "$m8_path/HFile/$class_name.php";
  require_once "$m8_path/Output/Output_css.php";
  $highlighter = new Core(new $class_name(), new Output_css());
  $lecode = nl2br($highlighter->highlight_text($texte));
  $lecode = ereg_replace("\t", "&nbsp; &nbsp; &nbsp; &nbsp; ", $lecode);
  $lecode = ereg_replace("  ", " &nbsp;", $lecode);
  return $lecode;
}

elle prend deux paramètre : le code à colorer, et le type du fichier (langage).

il vous faudra aussi ajouter ces styles à votre css [9] [11] :

dans habillage.css par exemple

.beautifier_linecomment {
	color: green;
}

.beautifier_blockcomment {
	color: green;
}

.beautifier_prepro {
	color: lightgreen;
}

.beautifier_select {
	color: red;
 font-weight: bold;
}

.beautifier_quote {
	color: coral;
}

.beautifier_1 {
	color: blue;
}

.beautifier_2 {
	color: cornflowerblue;
}

.beautifier_3 {
	color: #e5b23b;
}

.beautifier_4 {
	color: gray;
}

code de traitement du raccourcis

avant_propre et apres_propre ne changent pas beaucoup ici, on les adapte juste pour utiliser des balises au format
<ccode(nom_du_langage)> code à colorer </ccode>

le nom du langage etant le nom dans le fichier HFile, par exemple pour du php3, on a un fichier HFile_php3.php, on donnera donc le nom de langage php3.

dans ecrire/mes_options.php3

function avant_colorer_code($texte) {
   global $code_a_colorer;

   $regexp_echap = "(<ccode\\(([^\\)]+)\\)>(([^<]|<[^/]|</[^c]|</c[^c]|</cc[^o]|</cco[^d]|</ccod[^e]|</ccode[^>])*)<\/ccode>)";

   $colo_code_ech=0;
   while (preg_match($regexp_echap, $texte, $regs)) {
      $code_a_colorer[$colo_code_ech] = $regs[2];
      $langage = $regs[1];
      $pos = strpos($texte, $regs[0]);
      $texte = substr($texte,0,$pos).
                  "<@@colorercode_".
                  $langage.
                  "_$colo_code_ech@@>"
                  .substr($texte,$pos+strlen($regs[0]));
      $colo_code_ech++;
   }
   return $texte;
}

function apres_colorer_code($texte) {
   global $code_a_colorer;

   while (ereg('<@@colorercode_([^_]+)_([0-9]+)@@>', $texte, $regs)) {
      $lenum = $regs[2];
      $lecode = my_highlight_string($code_a_colorer[$lenum],$regs[1]);

      $pos = strpos($texte, $regs[0]);

      $texte = substr($texte, 0, $pos).
                    "<tt><div class='spip_ccode' dir='ltr'>". 
                    $lecode.
                    "</div></tt>".
                    substr($texte, $pos + strlen($regs[0]));
   }

   $find = "/&lt;@@SPIP_(SOURCEPROPRE|SOURCETYPO)([0-9]+)@@&gt;/";
   $replace = "<@@SPIP_\\1\\2@@>";
   $texte = preg_replace($find,$replace,$texte);

   return $texte;
}

Notes

[1Plus de 200 fichiers de configuration sont déjà disponibles.

[2voir l’annonce dans les archives de spip-core

[3du code perl ou C passe à peut pret, vue la similitude.

[4cette technique est justement celle utilisée par SPIP

[5à ajouter à votre fichier css

[6SPIP échappe le texte avec des expressions régulières. Il échappe tout ce qui se trouve entre balise html, code, cadre et poesie. Mais aussi tout code html qui pourrait être mal interprété par le moteur de typographie, soit, tout code html qui ressemble à <[a-zA-Z!][^<>!':;\?]*[!':;\?][^<>]*>

[7i.e. le navigateur cherchera à afficher une image appelée $source.

[8adapté à votre style

[9habillage.css par exemple

[10la condition d’utilisation est de mettre un lien sur votre site vers les sources.

[11et à ecrire/spip_style.php si vous voulez voir la tête du texte dans la partie privée du site.

Discussion

5 discussions

  • 1

    Pour ceux qui ne l’auraient pas vu, dans la partie « compliquée » il manque les fonctions avant_propre et apres_propre à récupérer de la partie « simple » et à mettre dans ecrire/mes_fonctions.php(3) également.

    Belle contribution en tout cas !

    Répondre à ce message

  • Maintenant on peut utiliser en plus du plugin officiel un autre plugin : SyntaxHighlighter pour SPIP

    Répondre à ce message

  • J’ai modifié ta contrib pour utiliser d’autres langages (colorisés à la sauce PHP). Cà peut être utile pour mettre en valeur du code HTML par exemple sans utiliser de programme externe.

    Répondre à ce message

  • 1
    Nicolas

    Tout d’abord merci pour cet article très intéressant.

    Par gout personnel, j’ai remplacé l’utilisation de beautifier par GeSHI. Pour cela, il suffit de modifier la fonction
    apres_colorer_code comme ceci :

    (...)
    while (ereg('<@@colorercode_([^_]+)_([0-9]+)@@>', $texte, $regs)) {
    $lenum = $regs[2];
    
    include_once('geshi.php');
    $geshi =& new GeSHi($code_a_colorer[$lenum], $regs[1]);
    $lecode = $geshi->parse_code();
    
    $pos = strpos($texte, $regs[0]);
    (...)

    Sinon, l’on souhaite une expression régulière plus courte en voici une :

    $regexp_echap = ",<ccode\((\w+)\)>(.*?)</ccode>,ims";
    • normalement, la fonction my_higlight_string et faite pour être modifiée et brancher un autre systéme. C’est peut être un peu plus simple de proposer une nouvelle fonction :

      function my_highlight_string($texte, $file='php3') {
      
      include_once('geshi.php');
      $geshi =& new GeSHi($code_a_colorer[$lenum], $regs[1]);
      return $geshi->parse_code();
      
      }

      je ne connaissait pas ce script, il a l’air vraiment interessant, est-ce que c’est facile d’ajouter un langage ?

    Répondre à ce message

  • 6

    « screenshot » se dit « copie d’écran » ; « syntaxe highlighting » se dit « surlignement syntaxique » ; etc... si si ! c’est important.

    • 100% d’accord avec toi, Hapax.

      Malgré tout je suis surpris que ce soit le seul commentaire pour cet intéressant article, qui une application de avant_propre et apres_propre. J’avais déjà lu les explications indiquées en tête de cet article, auxquelles je n’avais rien compris. Me voilà comblé maintenant. Reste plus qu’à utiliser cette contrib et à l’adapter à mes besoins.

    • Je veux afficher du code HTML, mais j’ai l’impression que c’est l’expression

      $regexp_echap = "(<ccode>(([^<]|<[^/]|</[^c]|</c[^c]|</cc[^o]|</cco[^d]|</ccod[^e]|</ccode[^>])*)<\/ccode>)";

      qui ne sait pas délimiter certaines séquences ...</ccode.

      Pour moi ce genre de regexp, c’est l’enfer ! Je me demande d’ailleurs pourquoi il n’existe pas quelque chose pour interdire une séquence de caractères. Peut-être avec une référence arrière ? mais je ne vois pas trop comment...

    • et fin de semaine alors !!

      bon je n’ai plus le droit d’ecriture sur l’article, ça tombe mal.

    • je crois qu’on peut faire cela. J’ai découvert après et jamais essayé :

         $regexp_echap = "(<ccode\\((.+)\\)>(.*)<\/ccode>)U";

      mais attention, il faut aussi reprendre les index plus loin.

      Qu’est ce qui passe pas comme code html en fait ?

    • Escuses. J’ai testé la regexp toute seule. Elle marche très bien. Donc elle n’est pas en cause...

      J’ai des balises HTML entre et le script en masque certaines. Je viens de trouver la raison pour certaines : les balises qui entourent une page HTML sont prises pour des raccourcis du même nom. Mais il y a d’autres choses que je ne comprends pas et SPIP met des @@SPIP_SOURCEPROPREXX@@ (sans <> autour) dans les $code_a_colorer[$colo_code_ech].

      Ayant utilisé Beautifier avec le fichier html fourni, comme ça ne fonctionnait pas, je suis passé à la méthode simple et encore sans highlight_text. J’utilise une méthode simple pour passer les balises HTML en < et >.

    • Ok, le problème est expliqué dans l’article :

      SPIP appelle sa fonction d’échappement des blocs « code », « html », « cadre » etc... avant celle fournit ici et je n’y peut rien :(

      La méthode, vraiment pas cool quand on doit utiliser <html>, c’est d’écrire <code><html></code>.

      La deuxième méthode, pas très cool non plus, c’est les &lt ; à la place des <.

      Dans les deux cas, beautifier sera perdu et ne pourra pas colorer les parties échappées.

    Répondre à ce message

Ajouter un commentaire

Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :

  • Désactiver tous les plugins que vous ne voulez pas tester afin de vous assurer que le bug vient bien du plugin X. Cela vous évitera d’écrire sur le forum d’une contribution qui n’est finalement pas en cause.
  • Cherchez et notez les numéros de version de tout ce qui est en place au moment du test :
    • version de SPIP, en bas de la partie privée
    • version du plugin testé et des éventuels plugins nécessités
    • version de PHP (exec=info en partie privée)
    • version de MySQL / SQLite
  • Si votre problème concerne la partie publique de votre site, donnez une URL où le bug est visible, pour que les gens puissent voir par eux-mêmes.
  • En cas de page blanche, merci d’activer l’affichage des erreurs, et d’indiquer ensuite l’erreur qui apparaît.

Merci d’avance pour les personnes qui vous aideront !

Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.

Qui êtes-vous ?
[Se connecter]

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

Ajouter un document

Suivre les commentaires : RSS 2.0 | Atom