(c) 2005 - Alexandre Brillant

Un éditeur ne doit pas se limiter à un ensemble de solutions uniques et non extensibles. Emacs rentre dans la catégorie des environnements évolutifs, il vous sert à concevoir un outil adapté à vos besoins. Je vous propose à travers cet article de vous familiariser avec le développement d'un mode mineur. Ce type de mode est incontournable pour améliorer votre productivité en réduisant les tâches répétitives. Cet article vous initie aussi brièvement au développement Lisp et à l'intégration d'une nouvelle fonctionnalité dans Emacs.


Développement d'un mode mineur pour Emacs

Quelques rappels sur emacs

Emacs (Editing MACroS) est un éditeur de texte apparu au MIT en 1975 sous l'impulsion de Richard Stallman. Plusieurs versions commerciales sont apparues dont CCA Emacs et Unipress Emacs, l'important étant à la fin l'émergeance de GNU Emacs avec la FSF (Free Software Foundation). Emacs est avant tout un éditeur de texte extensible disponible sur quasiment toutes les plate-formes (même Windows). Vouloir cerner les fonctionnalités d'Emacs n'a pas de sens puisque Emacs n'est qu'un intepréteur de lisp étendu (Emacs Lisp). On greffe au besoin des librairies sachant que la distribution GNU Emacs de base couvre la plupart des besoins d'édition (C, C++, Java,Perl, LateX ...).

Emacs gère des buffers correspondant à des copies mémoire de fichiers en cours d'édition. Chaque buffer fonctionne dans un mode majeur représentant un domaine d'édition. Par exemple le cc-mode est un mode majeur pour l'édition de code C ANSI. On associe à un mode majeur un ou plusieurs modes mineurs. Chaque mode mineur intervient dans une partie de l'édition pas forcément en rapport avec le mode majeur par exemple flyspell-mode réalise une vérification orthographique automatique. L'utilisateur dispose de plusieurs moyens d'intéraction avec le mode majeur. Il peut exécuter des commandes par la barre de menus, par une combinaison de touches ou par l'insertion d'une commande dans une zone appelée minibuffer. Les combinaisons de touches sont trés nombreuses et quoi qu'on en dise trés pratiques puisqu'il n'est plus nécéssaire de décoller les mains du clavier.

Quelques rappels sur le lisp

Avant d'entrer dans le vif du sujet, il est important d'avoir quelques notions de base sur le Lisp. Le Lisp (LISt Processing) est un langage employé depuis les années 50 pour l'IA. Sa syntaxe est particulièrement simple et repose sur l'usage de listes composées de parenthèses et d'expressions (elles-mêmes composés de listes). Pour évaluer une liste, il suffit de se placer à la fin d'une expression et d'utiliser la combinaison de touches C-x C-e (littéralement les touches Ctrl et x puis Ctrl et e).

Exemples :

(+ 1 2 3) C-x C-e
affichera 6 dans le minibuffer

(- 10 20 30) C-x C-e
affichera -40 dans le minibuffer

Le premier terme de la liste est considéré comme une fonction(+ et - ci-dessus) dont les paramètres sont les éléments restants (1 2 3 représentent trois paramètres pour la fonction +).

Pour interdire l'évaluation d'une liste en tant qu'appel de fonction, on utilise la fonction quote. La syntaxe supporte aussi par simplification le caractère '. La liste devient donc une valeur à part entière associée à un paramètre de fonction ou à une variable.

Exemples :

(quote (+ 1 2 3)) est équivallent à '(+ 1 2 3)

Pour accéder à une partie d'une liste, on dispose de nombreuses fonctions. Sans rentrer dans les détails, la fonction car accède au premier élément et la fonction cdr au reste de la liste.

Exemples :

(car '(1 2 3)) 
affiche 1
(cdr '(1 2 3)) 
affiche (2 3)

;; Fonction comptant le nombre d'éléments d'une liste
(defun compte-element(liste)
(if (null liste ) 0
(1+ (compte-element (cdr liste)))))

(compte-element '(1 2 3)) affiche 3

Pour comprendre précisément cette dernière expression, defun définie une fonction appelée compte-element prenant un paramètre appelé liste. La première ligne de la fonction teste si la liste est vide par la fonction null et returne 0 dans ce cas, sinon la ligne suivante est évaluée et retourne recursivement le résultat en l'incrémentant d'une valeur avec le reste de la liste obtenue par la fonction cdr.

Bien sûr compter le nombre d'éléments d'une liste existe déjà par la fonction length. Pour plus d'information, je vous conseille de vous reporter à l'aide intégré à Emacs accessible par la commande apropos ou par le menu Help.

Remarque importante : Toute expression en lisp est une fonction et retourne donc une valeur correspondant au dernier bloc de code évalué. Par exemple (if nil 1 2) retournera la valeur 2 puisque le test est sur la valeur nil qui oblige à passer dans le bloc "else" representé par l'expression 2.

Développement d'un mode mineur

Nous allons ici décrire l'ensemble des étapes nécessaires à la construction d'un mode mineur. Un exemple gérant un compteur de ligne automatique sera associée à cette étude.

Un mode mineur est associé à un mode majeur, il apparaît dans la ligne du mode ou barre de statut au-dessus du minibuffer lorsqu'il est dans l'état actif. L'activation et la désactivation revient souvent à appeler en commande du minibuffer le mode mineur postfixé par -mode. Par exemple le mode mineur abbrev trés pratique pour la completion automatique est activable par la commande abbrev-mode

Commençons la conception de notre mode mineur, tout d'abord il est nécessaire d'éditer un nouveau fichier par exemple 'count.el' (C-x C-f). L'extension .el va indiquer à Emacs (grâce à la variable auto-mode-alist) de passer dans le mode majeur Emacs Lisp qui offre des facilités de gestion (coloration syntaxique, debugger...).

Commençons par définir une variable représentant l'état de notre mode mineur (activé ou désactivé).

;; Variable indiquant si le mode mineur est actif ou non
(defvar count-mode nil
"Variable de notre mode mineur count affichant préfixant chaque ligne insérée par un compteur.")

Jusque ici pas de problème, la fonction defvar définit la variable count-mode comme "vide" grâce à la valeur nil. Cette valeur est l'équivallent de la liste vide (). On place ensuite un commentaire qui indiquera à l'utilisateur d'emacs la signification de la variable.

 ;; Fonction appelée par l'utilisateur mettant à jour la variable count-mode
1 (defun count-mode(&optional arg)
2 "Fonction activant le mode mineur"
3 (interactive "P")
4 ;; Mettre à jour la variable count-mode à chaque appel de cette fonction
5 (setq count-mode
6 (if (null arg)
7 (not count-mode)
8 (> (prefix-numeric-value arg) 0)))
9 ;; Commande à effectuée lorsque le mode mineur est activé
10 (if count-mode
11 (progn
12 ;; Appeler la commande count-manage-return pour chaque retour charriot
13 (local-set-key [return] 'count-manage-return)
14 (setq count-value 1))
15 ;; Enlever la relation entre la touche retour charriot et la commande count-manage-return
16 (local-unset-key [return])))

Là, cela se complique un peu, mais pas de panique, analysons le code ligne par ligne. Il s'agit d'une fonction prenant en paramètre un argument optionnel (on utilise la combinaison de touche C-u pour définir cette option dans le minibuffer). La ligne suivante (2) contient un commentaire décrivant le rôle de la fonction. La fonction interactive en ligne 3 indique à emacs que cette fonction peut être appelée dans le minibuffer par un utilisateur (en commençant par M-x ou Alt x). Les lignes 5 6 7 8 définissent la valeur de notre variable count-mode, principalement il s'agit de considérer la variable count-mode comme une variable booléenne dont on inverse la valeur à chaque appel. La valeur count-mode passe donc de la valeur nil à la valeur t puis de la valeur t à la valeur nil. La fonction prefix-numeric-value convertit le paramètre en nombre, cela permet par exemple d'activer le mode mineur en passant en paramètre une valeur supérieure à 0 et inversement pour une valeur inférieure ou égale à 0.

A partir de la ligne 10, on définit le rôle du mode mineur lorsque la variable count-mode est vraie (donc avec la valeur t). Ce code consiste à associer à la touche "retour charriot" (ici [return]) la commande count-manage-return. Donc à chaque fois que la touche "retour charriot" sera appuyée dans le buffer courant, la commande count-manage-return sera appelée. La variable count-value nous sert comme nous le verrons dans la suite à sauvegarder la valeur de notre compteur entre chaque ligne. A chaque activation de notre mode mineur, cette variable sera donc initialiser pour reprendre les numéros de ligne à partir de 1. Lorsque le mode mineur est désactivé (variable count-mode à nil), on enlève la gestion de la touche "retour charriot" par la commande local-unset-key.

;; Déclare le mode mineur count-mode dans la liste des modes mineurs accessibles
;; C'est ce code qui affiche dans la barre de status l'activation ou la désactivation
;; du mode mineur
(if (not (assq 'count-mode minor-mode-alist))
(setq minor-mode-alist
(cons '(count-mode " Count" )
minor-mode-alist)))

Cette ligne déclare notre mode mineur dans la liste minor-mode-alist. La fonction assq sert simplement à éviter de dupliquer l'ajout de notre mode mineur dans minor-mode-alist. C'est cette fonction qui indique dans la barre de statut courante que notre mode mineur est actif grâce à la chaîne " Count"

;; Compteur de ligne
(defvar count-value 1
"Variable pour le mode mineur count contenant un numéro de ligne")

Ici nous déclarons une variable contenant le compteur de ligne courant.

  ;; Affiche le compteur aprés chaque retour charriot
1 (defun count-manage-return()
2 "Cette fonction affiche le compteur aprés chaque retour charriot"
3 (interactive)
4 ;; Se placer au début de la ligne courante
5 (beginning-of-line)
6 ;; Insert le compteur
7 (insert (concat "(" (number-to-string count-value) ") "))
8 ;; Se positionner à la fin de la ligne courante
9 (end-of-line)
10 ;; Insérer le retour charriot
11 (insert "\n")
12 ;; Incrémenter le compteur
13 (setq count-value (1+ count-value)))

Cette fonction est importante, c'est elle qui gère l'affichage du compteur. Nous commençons par nous plaçer au début de la ligne courante avant que le retour charriot soit pris en compte grâce à la fonction beginning-of-line. Puis nous insérons le compteur grâce à la ligne 7. La commande insert ajoute donc une chaîne dans le buffer courant. Comme le lisp ne type pas les variables, nous devons convertir notre variable count-value en chaîne de caractère grâce à la fonction number-to-string. La fonction concat réalise une concaténation de tous les paramètres pour former une chaîne "(x)" avec x comme compteur de ligne. Enfin les lignes suivantes (10-13) consiste à se placer en fin de ligne, insèrer un "retour charriot" et incrémenter le compteur de ligne

Il ne reste plus qu'à tester notre mode mineur, commençons par évaluer notre code grâce à la commande eval-buffer. Appelons la commande count-mode grâce à M-x (touches Alt et x) et insérons un texte

(1) Cela
(2) Marche

Déclaration du mode mineur

Pour rendre ce mode mineur accessible au démarrage d'emacs, il faut ajouter la commande (provide 'count) à notre fichier. Cette commande rendra ce code intégrable par la commande (require 'count) dans le fichier de configuration d'emacs .emacs situé dans votre répertoire personnel.

Pour trouver votre fichier, emacs utilise la variable load-path. Cette variable peut être modifiée dans votre fichier de configuration, par exemple :(setq load-path (cons "~/emacs-lisp" load-path)) cherchera vos fichiers aussi dans le répertoire emacs-lisp de votre répertoire de travail.

Pour conclure

Le développement d'un mode mineur peut rendre de grands services. Emacs est un espace de création idéal, il vous sert à bâtir un environnement adapté et c'est aussi une bonne manière de se familiariser avec un nouveau langage. Les outils tout faits comme on en trouve aujourd'hui sont souvent des leurres et l'utilisateur finit pas être prisonnier voire handicaper par l'absence d'extension de l'outil.

L'auteur

Alexandre Brillant (abrillant@wanadoo.fr)

Ingénieur Indépendant de développement Java/XML

Site Perso : http://www.djefer.com

Web

Le source complet

Quelques liens sur Emacs et le Lisp

Une introduction au lisp

http://www.gnu.org/manual/emacs-lisp-intro/emacs-lisp-intro.html

Le manuel de référence GNU Emacs

http://www.gnu.org/manual/emacs-20.3/emacs.html

Le site officiel GNU Emacs

http://www.gnu.org/software/emacs/

Un site français autour de GNU Emacs

emacsFr.org

Une liste de librairies pour GNU Emacs

http://www.cogsci.ed.ac.uk/~stephen/ell.html

Une liste de ressource sur GNU Emacs

http://www.gnusoftware.com/Emacs/

Exemple de configuration Emacs

http://www.djefer.com/info/emacs

Newsgroup pour emacs :

- gnu.emacs.help

- gnu.emacs.sources

- fr.comp.applications.emacs

GNU Free Documentation License

http://www.fsf.org/licenses/fdl.html