[ précédent ] [ Table des matières ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ 13 ] [ suivant ]


À la découverte de GNU Emacs
Chapitre 5 - Introduction à ELisp, le dialecte Lisp d'Emacs


Ce chapitre a pour but de vous apprendre les bases juste nécessaires afin de pouvoir utiliser le GNU Emacs Lisp Reference Manual pour découvrir le reste. Le GNU Emacs Lisp Reference Manual est extrêmement bien fait mais ce n'est pas un tutoriel : en lecture linéaire, il faut attendre très longtemps avant de savoir écrire sa première fonction, ce qui n'est pas très pratique quand on débute...

Ce chapitre doit donc vous permettre de comprendre de quoi on parle en ELisp afin que vous puissiez par la suite parcourir sans peine le GNU Emacs Lisp Reference Manual à la recherche d'informations plus précises. Il pourra également être utile à ceux qui aimeraient comprendre ce qu'ils mettent dans leur .emacs.el.


5.1 Courte présentation

Lisp signifie LISt Processor : l'essentiel des opérations se fait en manipulant des listes comme (34 12 (+ a b)) qui est une liste composé des entiers 34, 12 et d'une liste elle-même composée des symboles +, a et b. On peut arriver, surtout si on n'aère ou n'indente pas son code source, à quelque chose de peu lisible, si bien que certains disent de Lisp que ce terme signifie « Lots of Insipid and Stupid Parentheses. »

Lisp n'est pas un langage. C'est une famille de langages et ELisp (Emacs Lisp) est un dialecte Lisp qui donne accès aux fonctionnalités offertes par Emacs. C'est un langage interprété (donc très dynamique). On peut toutefois byte-compiler un fichier ELisp afin d'accélérer son chargement (voir Gestion des fichiers ELisp, Section 5.9).


5.2 Approche de la syntaxe, évaluation

Quand l'interpréteur Lisp évalue une liste, il évalue récursivement chaque élément de cette liste sauf contre-indication. L'évaluation d'une liste dont le premier élément est un nom de fonction cause un appel de cette fonction avec pour arguments les autres éléments de la liste et produit comme résultat la valeur de retour de cette fonction. Par exemple, lors de l'évaluation de :

     (- (* 3 (1+ 4) 2) (/ 4 2))

l'interpréteur Lisp trouve dans la première liste le symbole -, qui est le nom d'une fonction de base d'Emacs (fonction soustraction) et l'appelle donc avec deux arguments : (* 3 (1+ 4) 5) et (/ 4 2). Le premier est aussi un appel de fonction : multiplication de 3 par (1+ 4) et par 2, c'est-à-dire de 3 par 5 et par 2 (car 1+ est le nom d'une fonction qui ajoute 1 à son argument). Ce premier argument vaut donc 30. De même, le deuxième argument de - est le résultat de la division de 4 par 2 donc l'expression entière donne par évaluation 3*5*2-4/2 c'est-à-dire l'entier 28. Mettez-vous dans le buffer *scratch*, tapez l'expression ci-dessus, placez le point après la parenthèse fermante, tapez C-j ou C-x C-e : vous voyez s'afficher 28 (dans le buffer si vous avez choisi C-j, dans l'echo area si vous avez choisi C-x C-e).

On peut également, en principe dans n'importe quel mode, évaluer une expression ELisp avec M-: (eval-expression). L'expression doit être saisie dans le minibuffer et le résultat de son évaluation est affiché (dans la mesure du possible) dans l'echo area. C'est assez pratique dans certains cas.

Les fonctions sont identifiées par des symboles qui sont leurs noms. Certains noms désignent des fonctions un peu particulières appelées special forms (tout objet Lisp prévu pour être évalué est appelé une expression ou encore une form -- en anglais) : lors de l'évaluation d'une special form, certains de ses arguments ne sont pas évalués (contrairement à ce qui se passe dans le cas d'une fonction). Ainsi, quand l'interpréteur Lisp évalue :

     (setq ma-variable (+ 3 4))

il n'évalue pas le symbole ma-variable car setq est une special form qui n'évalue pas son premier argument (ceci peut bien sûr être trouvé en plus complet dans le GNU Emacs Lisp Reference Manual). Par contre, setq évalue son deuxième argument, qui vaut ici 3+4, c'est-à-dire 7. Finalement, cette expression affecte la valeur 7 à la variable ma-variable. La valeur de l'expression est 7 (par définition de setq -- voir le GNU Emacs Lisp Reference Manual) et peut donc être utilisée dans un autre appel à setq. Par exemple :

     (setq machin (setq truc 5))

affecte 5 aux variables truc et machin (dans cet ordre).

Il est très important que setq n'évalue pas son premier argument. En effet, dans l'exemple :

     (setq machin "bla")

si setq évaluait tous ses arguments comme une fonction ordinaire, il y aurait probablement une erreur : soit parce que machin n'aurait pas de valeur (variable non initialisée), soit parce que machin serait d'un type inadéquat pour désigner le nom d'une variable (par exemple, machin pourrait être un entier ou une chaîne de caractères, donc on dirait d'affecter la valeur "bla" à l'entier 3 par exemple).

En fait, c'est précisément à cause de ce comportement que setq a un nom si bizarre : la fonction set est une fonction d'affectation, une vraie fonction, pas une special form : elle provoque l'évaluation de chacun de ses arguments, ce qui est peu pratique en général pour affecter une valeur à une variable. Le q de setq signifie quoted, c'est-à-dire « mis entre guillemets. » En effet, traditionnellement, dans le shell Unix ou certains langages de programmation, on met une expression entre guillemets quand on veut la protéger de l'évaluation : qu'elle reste intacte lors d'une évaluation.

Pour quoter en ELisp, on dispose de plusieurs méthodes. La première, pas très souple, consiste à utiliser une special form mais vu qu'on ne fait pas ce qu'on veut de l'argument quoté, ce n'est pas une méthode générale. Deux autres méthodes sont d'utiliser la fonction quote (à un seul argument) ou son abréviation ' (une apostrophe).

Mettez-vous dans le buffer *scratch*, tapez

     (quote (+ 3 4))

et évaluez cette expression : vous obtenez (+ 3 4) et non 7 : la Lisp form (+ 3 4) a été protégée de l'évaluation par la fonction quote. Vous obtenez le même résultat en évaluant '(+ 3 4).

Évaluez '''12 (ou (quote (quote (quote 12))), ce qui est équivalent) : vous obtenez (quote (quote 12)). Évaluez cette expression à son tour : vous obtenez (quote 12). Évaluez (quote 12) : vous obtenez 12. Évaluez 12 : vous obtenez 12. Les entiers sont invariants par évaluation. On dit qu'ils sont self-evaluating. En fait, toute Lisp form qui n'est ni une liste, ni un symbole (voir Les symboles, Section 5.3) est self-evaluating. Donc les nombres, les chaînes de caractères comme "Emacs est génial" ou les arrays comme [12 3 42] sont invariants par évaluation.

Finalement, pour affecter la valeur 3 à machin, vous avez de nombreuses possibilités :

     (setq machin 3)
     (set 'machin 3)
     (setq machin '3) ; car '3, identique à (quote 3), est évalué pour donner 3
     (set 'machin '3)

ou encore beaucoup plus tordu :

     (setq nom 'machin)
     (set nom 3)

set attend comme premier argument un symbole qui est le nom de la variable à affecter. Ici, le résultat de l''évaluation de nom dans (set nom 3) est le symbole machin donc on affecte bien 3 à la variable machin.


5.3 Les symboles

Un symbole (symbol en anglais) est un type de données Lisp fondamental permettant de nommer les fonctions et les variables, aussi bien que d'être utilisé tel quel. Le nom en question est une suite de caractères choisis parmi un ensemble précisé dans le GNU Emacs Lisp Reference Manual.

On dit qu'un symbole a plusieurs composantes (appelées components ou cells en anglais), entre autres les trois suivantes :

Quand Emacs évalue une liste, il cherche la function cell du symbole qui est le premier élément de la liste (si c'est un symbole, sinon il y a une erreur) pour accéder à la fonction en question. Si cette function cell est vide, on obtient le message d'erreur bien connu : Symbol's function definition is void: <nom-du-symbole>. Pour les autres éléments de la liste, il cherche dans la value cell afin de déterminer la valeur des arguments correspondants, d'où le message d'erreur encore plus célèbre quand on tente d'évaluer une variable à laquelle aucune valeur n'a été affectée : Symbol's value as variable is void: <le-nom-du-symbole>.

Finalement, dans une première approche, les symboles n'ont rien de compliqué : ce sont des noms comme toto, machin, global-font-lock-mode, etc. permettant d'accéder soit à une variable, soit à une fonction, selon le contexte dans lequel ils sont utilisés.

Il existe en ELisp deux symboles particuliers : t et nil. Ces symboles n'ont pas de function cell ni de value cell. On ne peut pas les utiliser comme noms de fonctions. Par contre, il sont fréquemment utilisés comme arguments à des fonctions ou comme résultat de l'évaluation d'une fonction (t signifiant souvent true, c'est-à-dire « vrai » et nil « faux »).

On utilise parfois les symboles directement pour leur nom et non pour leur function cell ou leur value cell. Par exemple, j'ai dans mon .emacs.el pour préciser la taille du papier pour certaines fonctions de génération de fichiers prêts à être imprimés (PostScript) la ligne suivante :

     (setq ps-paper-type 'a4)

Le cas le plus fréquent d'utilisation d'un symbole tel quel est cependant celui de t ou nil, comme dans le cas de

     (setq next-line-add-newlines nil)

qui permet de signaler à Emacs qu'on ne veut pas (nil) qu'il ajoute des lignes à un buffer quand on demande d'aller à la ligne suivante alors qu'on est déjà à la fin de ce buffer.


5.4 Manipulation des variables

Nous avons déjà vu l'essentiel sur ce sujet en Approche de la syntaxe, évaluation, Section 5.2. Nous avons en particulier déjà parlé de setq pour affecter une valeur à une variable. On peut appeler cette special form avec plus de deux arguments pour affecter plusieurs valeurs à plusieurs variables :

     (setq variable1 valeur1
           variable2 valeur2
           ......... .......
           variablen valeurn)

Nous n'avons cependant pas encore évoqué l'existence de variables locales à un buffer (en anglais : buffer-local variables). Il s'agit de variables (comme fill-column) qui peuvent avoir plusieurs valeurs différentes dans plusieurs buffers à un moment donné d'une session Emacs. Quand on évalue (setq variable valeur) dans un buffer et que variable est buffer-local, on n'affecte pas la valeur de cette variable dans les autres buffers.

Mais que se passe-t-il quand on crée un buffer ? Quelle valeur prend une variable buffer-local dans ce nouveau buffer ? Réponse : sa valeur par défaut (en anglais, default value). Pour changer cette dernière, on utilisera setq-default au lieu de setq.

Il y a quelques petits détails supplémentaires à ce sujet que vous trouverez si besoin dans le GNU Emacs Lisp Reference Manual (notamment comment faire en sorte qu'une variable soit locale à un buffer).


5.5 Écrire une fonction

On distingue essentiellement deux types de fonctions en ELisp : les fonctions qui sont dans la function cell d'un symbole, autrement dit celles qui ont un nom (celui du symbole) et les fonctions anonymes, qui sont utilisées soit temporairement, soit stockées dans une liste, etc.


5.5.1 Fonctions nommées

Pour définir une fonction nommée, on dispose de la special form defun dont voici une syntaxe simplifiée :

     (defun nom-de-la-fonction (argument1 argument2 ... argumentn)
       "Chaîne de caractère pour documenter la fonction"
       (Lisp form1)
       (Lisp form2)
       ............
       (Lisp formn)
       )

Par exemple, voici une fonction qui prend deux arguments supposés être des chaînes de caractères et qui retourne la concaténation de ces deux chaînes de caractères :

     (defun fonction-inutile (ch1 ch2)
       "Fonction inutile pour concaténer deux chaînes de caractères.
     Cette fonction est inutile car la fonction concat fait bien mieux
     ce travail. C'est juste un exemple. D'ailleurs, cette fonction fait
     appel à concat..."
       (concat ch1 ch2))

Ici, il n'y a qu'une Lisp form dans le corps de la fonction : (concat ch1 ch2). Évaluez l'expression entière de définition de fonction-inutile dans le buffer *scratch* puis évaluez par exemple (fonction-inutile "abc" "def") : vous obtenez le résultat attendu, à savoir "abcdef".

La chaîne de caractères de documentation est optionnelle mais si vous avez l'intention de distribuer votre programme, il est fortement conseillé de la mettre. On peut spécifier des arguments optionnels de la manière suivante :

     (defun fonction-inutile (ch1 ch2 &optional ch3 ch4)
       "Fonction inutile pour concaténer deux chaînes de caractères.
     Cette fonction est inutile car la fonction concat fait bien mieux
     ce travail. C'est juste un exemple. D'ailleurs, cette fonction fait
     appel à concat..."
       (if (and (not (null ch3))
                (not (null ch4)))
           (message (concat "Plonk ! " ch3 ch4))
         (goto-char (point-min))
         )
       (concat ch1 ch2))

Ici, ch3 et ch4 sont optionnels. La fonction commence par tester si ces arguments ont été fournis lors de l'appel (sinon, Emacs leur attribue la valeur nil)[24]. Si oui, elle affiche dans l'echo area un message composé à l'aide de ces arguments, sinon elle déplace le point au début du buffer courant (oui, c'est vraiment une fonction inutile). Puis elle retourne comme la précédente la concaténation de ses deux premiers arguments.

Les fonctions définies en ELisp peuvent même être déclarées comme admettant un nombre quelconque d'arguments optionnels : voir le GNU Emacs Lisp Reference Manual.

Les fonctions que nous venons de définir sont bien jolies, mais elles ne peuvent être appelées que par du code ELisp : ce ne sont pas des commandes. Pour transformer une fonction en commande, afin qu'elle puisse être appelée par M-x nom-de-la-fonction, il suffit d'ajouter une interactive declaration au début de la fonction comme dans l'exemple suivant :

     (defun ma-fonction ()
       "Fonction d'exemple.
     Fonction qui amène le point juste après le prochain nombre entier (écrit en
     décimal) du buffer courant."
       (interactive)
       (save-match-data
         (re-search-forward "[0-9]+")))

On a simplement rajouté le (interactive) comme toute première Lisp form de la définition de ma-fonction.

L'appel à save-match-data sert juste à être propre car la fonction re-search-forward met à jour la match data, qui mémorise des informations sur la dernière recherche faite avec des regular expressions (chercher « Regexps » dans le GNU Emacs Manual), par exemple pour savoir qu'est-ce qui a été trouvé (où ça commence, où ça se termine, etc.). Si ma-fonction est appelée entre deux Lisp forms qui travaillent sur la match data, on est sûr grâce au save-match-data qu'elle ne perturbera pas la deuxième fonction. Ceci n'est pas très important à votre stade supposé de la connaissance d'ELisp. C'est juste pour ne pas donner de mauvais exemple ni laisser de point obscur. :-)

Dans le cas précédent, transformer la fonction en commande était donc extrêmement simple : rajout de (interactive). Cela se complique dans le cas d'une fonction qui prend au moins un argument obligatoire car il faut préciser comment elle les obtient lorsqu'on invoque M-x nom-de-la-fonction. Cela peut se faire dans la plupart des cas en appelant interactive avec une chaîne de caractères en argument mais on peut procéder différemment. Voir le GNU Emacs Lisp Reference Manual à la section Using interactive pour plus d'informations. Voici un exemple de fonction qui prend deux arguments : des numéros de caractères dans le buffer courant (ils servent donc à préciser deux « endroits » du buffer) et qui efface tout le texte compris entre ces deux positions. Quand la fonction est invoquée par M-x efface, les deux positions sont les extrémités de la region (tiens, r comme region, c'est marrant...).

     (defun efface (pos1 pos2)
       "Chaîne de documentation..."
       (interactive "r")
       (delete-region pos1 pos2))

Pour finir, quelques règles de bonne conduite quand vous définissez des fonctions :

Il y a un certain nombre d'autres règles de bonnes conduite visant à assurer la cohérence au sein d'Emacs, vous les trouverez dans le GNU Emacs Lisp Reference Manual, à la section Tips and Conventions.


5.5.2 Fonctions anonymes

Comme leur nom l'indique, les fonctions anonymes n'ont pas de nom. On les appelle aussi des lambda expressions. Voici comment définir une telle fonction :

     (lambda (arg1 arg2 argn)
       (Lisp form1)
       (Lisp form2)
       .............
       (Lisp formn))

Par exemple, si vous évaluez :

     (
      (lambda (arg1 arg2 arg3)
        (* (+ arg1 arg2) arg3))
      2 4 3)

vous obtenez le résultat de l'appel d'une fonction anonyme qui ajoute ses deux premiers arguments et multiplie le résultat par le troisième, appelée avec les arguments 2, 4 et 3. On obtient donc (2+4)*3, soit 18.

Jusqu'ici, les fonctions anonymes n'ont pas l'air très utiles. Elles le deviennent quand on programme et qu'on a besoin d'une fonction qui ne sert qu'une fois au milieu d'une autre. Elles sont également très pratiques quand on souhaite ajouter des fonctions à un hook, par exemple pour configurer le processus d'entrée dans un mode donné.

En effet, tout mode machin digne de ce nom définit un hook appelé machin-mode-hook[25] qui permet « d'accrocher » (hook signifie crochet) à un événement des fonctions définies par l'utilisateur. L'événement en question peut être l'ouverture d'un buffer dans le mode machin, par exemple.

Ainsi, avec la ligne suivante dans mon .emacs.el :

     (add-hook 'text-mode-hook 'turn-on-auto-fill)

j'active le mode mineur auto-fill-mode dans tout buffer qui passe dans le mode majeur Text (text-mode).

L'utilisation de lambda expressions permet d'ajouter des fonctions plus complexes à un hook sans avoir à les déclarer comme fonction nommées. Par exemple :

     (add-hook 'sgml-mode-hook
               #'(lambda ()
                   (when (featurep 'psgml-init)
                       (require 'flo-psgml)
                       (define-key sgml-mode-map "\C-cf"
                         'flo-fill-debiandoc-sgml-item))))

fait des petites choses à l'initialisation du sgml-mode. Le # est optionnel, il sert à préciser que ce qui suit (et qui est quoté) est une fonction et peut donc être byte-compilé sans crainte si le fichier l'est. C'est un peu mieux que de mettre un simple quote (').


5.6 Compléments sur les listes

Comme vous l'avez compris, la liste est un type de données fondamental pour l'interpréteur ELisp. Si vous faites un peu d'ELisp, vous aurez vite besoin de manipuler une liste ou de comprendre certaines fonctions aux noms peu évocateurs comme car ou cdr.

Je vais donc vous expliquer ici ce qu'il est important de connaître sur les listes sans être évident (pour les choses simples du genre récupérer le n-ième élément d'une liste, je vous laisse vous reporter à la section adéquate du GNU Emacs Lisp Reference Manual).


5.6.1 Les cons cells

La liste au sens d'ELisp est construite de manière récursive, comme nous allons bientôt le voir, à partir de cons cells. Une cons cell est une structure manipulée par ELisp contenant deux références (deux pointeurs) vers deux objets ELisp quelconques. Ces deux objets sont appelés le CAR et le CDR de la cons cell (pour des raisons qui tiennent à l'histoire du Lisp).

Pour créer une cons cell ayant pour CAR l'entier 2 et pour CDR la chaîne de caractères "abcd", il suffit d'écrire en ELisp : (2 . "abcd") (ceci est appelé la dotted pair notation).

On peut également utiliser la fonction cons de la manière suivante :

     (cons 2 "abcd")

La différence importante entre ces deux méthodes est que cons étant une fonction, arg1 arg2 sont évalués lorsqu'on appelle (cons arg1 arg2), contrairement à ce qui se passe lorsqu'on utilise la dotted pair notation.

Les cons cells peuvent être imbriquées (puisque le CAR et le CDR contiennent des références vers n'importe quel objet ELisp). Lorsque vous évaluez une expression du type '(2 . (3 . 4)), vous obtenez (2 3 . 4) et non (2 . (3 . 4)). Ces deux objets sont les mêmes. Ce sont simplement deux façons différentes de les représenter. De même, on pourra écrire avantageusement (2 3 4 5 . 6) plutôt que '(2 . (3 . (4 . (5 . 6)))) qui représente le même objet ELisp.

On peut accéder au car d'une cons cell avec la fonction (ô surprise !) car. Par exemple, le résultat de l'évaluation de (car '(2 3 4 5 . 6)) qui, rappelons-le, est une expression équivalente à (car '(2 . (3 . (4 . (5 . 6))))), est l'entier 2.

De même, la fonction cdr permet d'accéder au CDR d'une cons cell. Par exemple, (cdr '(2 3 4 5 . 6)) donne comme attendu (3 4 5 . 6).


5.6.2 La Vraie Nature des listes

Une liste en ELisp est définie de manière récursive :

La liste (12 42 "WAP" consultant) est en fait une cons cell dont le CAR est l'entier 12 et le CDR une cons cell dont le CAR est 42 et le CDR une cons cell dont le CAR est la chaîne de caractères "WAP" et le CDR une cons cell dont le CAR est le symbole consultant et le CDR nil.

C'est immonde, vous ne me croyez pas. Évaluez donc :

     '(12 . (42 . ("WAP" . (consultant . nil))))

ou encore :

     '(12 42 "WAP" consultant . nil)

qui, comme nous l'avons vu en Les cons cells, Section 5.6.1, lui est équivalent. Vous obtenez la liste (12 42 "WAP" consultant).

Cette représentation est tout à fait générale et permet donc sans problème d'inclure des listes comme éléments d'autres listes. Par exemple, la structure de la liste (12 42 ("WAP" consultant)) est représentée par l'expression :

     '(12 . (42 . (("WAP" . (consultant . nil)) . nil)))

dont l'évaluation conduit bien à (12 42 ("WAP" consultant)).

Si cela vous semble obscur, sachez qu'il y a des petits dessins pour illustrer la structure des listes dans le GNU Emacs Lisp Reference Manual...


5.7 Quelques mots sur les chaînes de caractères

Je serai assez bref dans cette section. Je vais seulement parler un peu des caractères en ELisp et de la read syntax des chaînes de caractères ELisp, c'est-à-dire la syntaxe que vous pouvez utiliser dans un programme ELisp pour les représenter.


5.7.1 Les caractères

Un caractère en ELisp est un simple entier. Une read syntax pratique pour un caractère est, plutôt que d'utiliser son code, celle mettant en jeu un point d'interrogation : ainsi, ?a est une read syntax pour le caractère a et est équivalente à 97, le code ASCII (en décimal) de ce caractère.

On peut construire une chaîne de caractères à partir de caractères avec la fonction string :

     (string ?A ?a ?b 97 ?c ?d)

donne après évaluation la chaîne de caractères "Aabacd".

Vous trouverez beaucoup plus d'informations sur la read syntax des caractères au début du GNU Emacs Lisp Reference Manual si besoin.


5.7.2 La read syntax des chaînes de caractères

Cette read syntax traite le caractère antislash (\) de manière particulière. Entre autres :

Par exemple :

     "a3\\nt\
     b\"c\nd"

est une read syntax pour la chaîne de caractères composée des caractères a, 3, \, n, t, b, ", c, newline et d.

Les backslash escape-sequences utilisables dans les chaînes de caractères sont les mêmes que celles utilisables pour les caractères eux-mêmes et leur liste complète se trouve donc à la section Character Type du GNU Emacs Lisp Reference Manual.


5.8 Quelques constructions pratiques

Je vais juste mentionner ici l'existence de quelques constructions très pratiques en ELisp qu'il est indispensable de connaître dès qu'on programme un peu. Je vous fais par contre confiance pour trouver vous-même dans le GNU Emacs Lisp Reference Manual les special forms « évidentes » comme while, if ou cond qui fournissent les structures de contrôle habituelles d'un langage de programmation.

La première est introduite par la special form let (le terme special form est expliqué dans Approche de la syntaxe, évaluation, Section 5.2). Par exemple :

     (let (a bla c d)
       (Lisp form 1)
       (Lisp form 2)
       ..............
       (Lisp form n)
       )

est une Lisp form dont l'évaluation provoque l'évaluation successive de (Lisp form 1)... (Lisp form n) avec a, bla, c, et d comme variables locales (locales au let). La liste suivant le let permet quelques variations sur la syntaxe. Par exemple, avec :

     (let (a (bla "kjk") (c 3) d)
       (Lisp form 1)
       (Lisp form 2)
       ..............
       (Lisp form n)
       )

bla et c sont initialisées (localement au let) avec les valeurs "kjk" et 3, respectivement.

Il existe une variante de let, à savoir let*, qui permet d'initialiser une variable en fonction d'une variable précédemment initialisée par ce même let* (dans l'exemple précédent, cela permettrait d'utiliser la valeur affectée à bla, c'est-à-dire "kjk", pour initialiser c).

La deuxième construction dont je tiens à vous signaler l'existence est introduite par la special form progn. Elle permet de construire un bloc d'instructions ELisp, tout comme let, mais est plus simple car ne permet pas directement la création de variables locales à ce bloc (elle le permet indirectement bien sûr, en mettant un appel à let dans le progn). Par exemple, la Lisp form suivante :

     (progn
       (setq machin "e-")
       (setq machin (concat machin "commerce"))
       (message machin))

provoque après évaluation l'affectation de la valeur "e-commerce" à la variable machin puis l'affichage du message "e-commerce" dans l'echo area (l'affectation n'est a priori pas locale, donc c'est plutôt sale ; ce n'est qu'un exemple).

Ceci est très pratique, notamment quand on sait que la syntaxe de la special form if est :

     (if condition then-form else-forms...)

En effet, il n'y a qu'une seule then-form, donc un progn s'avère bien pratique pour faire plusieurs choses dans la clause then.

La troisième construction dont je souhaite vous signaler l'existence avant que vous ne partiez seul(e) dans l'exploration du GNU Emacs Lisp Reference Manual est introduite par la special form save-excursion. En fait, il existe pas mal de special forms très utiles dont le nom commence par save.

save-excursion est à utiliser dans pratiquement toute fonction qui déplace le point et/ou la marque et/ou change de buffer. L'évaluation de :

     (save-excursion
       (Lisp form 1)
       (Lisp form 2)
       ..............
       (Lisp form n)
       )

provoque l'évaluation de (Lisp form 1)... (Lisp form n) et fait en sorte que le buffer courant, la position du point et celle de la marque soient inchangés avant et après l'évaluation cette Lisp form. Pour être précis, c'est l'identité et non le contenu du buffer courant qui reste inchangée.

Cette fonction est indispensable si on veut programmer proprement. Pour citer le GNU Emacs Lisp Reference Manual : « It is used more than 4000 times in the Lisp sources of Emacs. »

Il existe d'autres fonctions très utiles dans le même ordre d'idées. Parmi elles, unwind-protect, with-temp-buffer, with-temp-file, with-current-buffer, save-match-data, ...


5.9 Gestion des fichiers ELisp

Si vous avez lu les sections précédentes, vous savez écrire un peu d'ELisp et vous vous demandez certainement comment stocker vos définitions de fonctions dans des fichiers afin de pouvoir les charger lors d'une session Emacs ultérieure (et d'en faire profiter toute la communauté Emacs, merci d'avance).

Il suffit pour cela de placer le code ELisp dans un fichier d'extension .el. Pour le charger, on utilisera load ou require en conjonction avec provide. Notez que la fonction locate-library permet de savoir si un fichier peut être trouvé dans le load-path.

Si vous écrivez un fichier ELisp, vous avez intérêt une fois que vous l'estimez stable à le byte-compiler, par exemple avec l'entrée « Byte-compile This File » du menu Emacs-Lisp présent en mode Emacs-Lisp. La byte-compilation d'un fichier toto.el produit un fichier toto.elc (ELisp Compiled) qui est chargé plus rapidement que toto.el (avec load, require, etc. qui détectent automatiquement la présence d'un fichier .elc lorsqu'on ne précise pas l'extension).


[ précédent ] [ Table des matières ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ 13 ] [ suivant ]


À la découverte de GNU Emacs

Version 1.36 (3 mai 2016)

Florent Rougon mailto:f . rougon (**AT**) free [point] fr