Le démarrage d’Emacs
Rédigé par Fred - - Aucun commentaireIntroduction
Il y a quelques temps de cela (je viens de me rendre compte que cela
fait plus de 10 ans déjà…), je commençais à utiliser Emacs. Et, comme
beaucoup je suppose, pour configurer, je copiais/collais des bouts de
code dans mon .emacs
sans chercher à comprendre ce que ça faisait
(comment ça c’est encore le cas ‽). Typiquement, j’avais plein de :
(autoload 'php-mode "php-mode" "Major mode for editing php code." t) (add-to-list 'auto-mode-alist '("\\.php$" . php-mode))
plutôt obscurs.
Et au fil du temps, les lignes s’accumulaient, et Emacs n’était plus aussi rapide à charger.
Je me suis alors intéressé à comment le rendre plus rapide au démarrage. Bien que je me sois rendu compte que la meilleure solution pour moi serait d’utiliser emacsclient1, j’ai d’abord fait du nettoyage, en utilisant le plus possible customize et en regroupant ça dans un seul fichier avec :
(setq custom-file "~/.emacs.d/config/custom.el") (load custom-file)
Puis j’ai supprimé tout ce dont je ne me servais en fait pas, et je me suis penché sur ce qu’il se passe au démarrage d’Emacs, pour savoir si je ne lui faisais pas faire des choses inutiles. Et, avec les différentes versions d’Emacs certainement, j’ai bien vu que la plupart de ces lignes étaient (devenues ?) inutiles.
tl;dr
Que fait autoload
? Qu’y a-t-il dans auto-mode-alist
? Le bout de
code précédent pour avoir le mode python est-il nécessaire ?
suffisant ? Peut-on faire autrement ? C’est à ça que je vais
m’intéresser par la suite. Pour cela, je me suis penché dans le code
et le manuel d’Emacs et je vous partage ce que j’en ai retenu et
laisse des informations pas forcément pertinentes, mais fortement en
relation et toujours instructives.
Pour ceux qui ne veulent pas tout lire : ça se passe là.
Table of Contents
- Introduction
- Comprendre ce qui est chargé et lancé au démarrage.
- Savoir comment charger une fonction/un fichier.
- How Programs Do Loading: The load function and others.
- Load Suffixes: Details about the suffixes that load tries.
- Library Search: Finding a library to load.
- Loading Non-ASCII: Non-ASCII characters in Emacs Lisp files.
- Autoload: Setting up a function to autoload.
- Repeated Loading: Precautions about loading a file twice.
- Named Features: Loading a library if it isn't already loaded.
- Where Defined: Finding which file defined a certain symbol.
- Unloading: How to "unload" a library that was loaded.
- Hooks for Loading: Providing code to be run when particular libraries are loaded.
- En bref
- Comment est choisi le mode d’un fichier
- Hooks
- Conclusion
Comprendre ce qui est chargé et lancé au démarrage.
Cette partie ne se focalise pas sur tout ce qui est fait au
démarrage, mais mets l’accent sur qu’est-ce que charge Emacs, et où il
va chercher ce qu’il charge, pour mieux comprendre comment faire son
.emacs
, et qu’il soit plus léger/rapide (charger un fichier =
ouvrir + évaluer tout ce qu’il y a dedans).
Un bon résumé des opérations effectuées par Emacs est disponible, comme toujours, dans le manuel : Startup-Summary (manuel elisp).
La variable top-level
contient la fonction (en fait la form
, objet
Lisp qui est sensé être évalué ~ expression, mais je laisserai form
par la suite) qui sera lancée au démarrage. Par défaut, c’est
normal-top-level
.
normal-top-level
La fonction normal-top-level
est donc appelée au lancement
d’Emacs. On peut la voir dans le fichier startup.el
2 (pour
trouver le fichier, taper dans Emacs M-x find-library startup.
Ou, via l’aide avec C-h f normal-top-level (juste q pour fermer
les fenêtres d’aide), si emacs2x-el
est installé, la première
ligne devrait être normal-top-level is a compiled Lisp function in
`startup.el'.
avec startup.el
qui est un lien).
Elle :
- fait diverses configurations tout au long de son appel (qui ne nous concerne pas pour le sujet présent) ;
- cherche des
subdirs.el/leim-list.el
dans chaque dossier deload-path
et les charge3 ; - lance la fonction
command-line
; - lance des hooks comme
emacs-startup-hook
etwindow-setup
.Bref, elle initialise, remplit un peu plus la variable
load-path
, et surtout délègue àcommand-line
.
command-line
Aussi située dans startup.el
, cette fonction :
- fait aussi des configurations tout du long ;
- initialise
before-init-time
; - met en chemin absolu les noms de fichiers dans
load-history
(C-h v load-history pour prendre peur) ; - definit le fichier init de quel utilisateur il faut utiliser ;
- parse quelques options de la ligne de commande (
-d
,-D
,-q
,-Q
,-nbc
, etc.) ; - lance
before-init-hook
; - charge
site-run-file
; - cherche le fichier init (cf plus tard) s’il existe et le lit
(puis
default.el
) ; - charge le fichier
abbrev
, s’il existe et qu’Emacs n’est pas en-batch
; - si un dossier de package existe, initialise le système de package
(
package-initialize
) ; - initialise
after-init-time
; - lance
after-init-hook
; - lance
command-line-1
(avec le reste des arguments) ; - appelle
kill-emacs
si option-batch
; - lance le serveur, si Emacs est lancé en démon ;
- lance
emacs-session-restore
.
C’est donc là que sont chargés les fichiers de configuration : site
puis init
et default
. Il faut voir également les hooks
before-init-hook
et after-init-hook
(qui sont définis à nil
par
défaut, mais dont la valeur peut changer selon les systèmes Init-File
(manuel elisp))
La fonction command-line-1
boucle pour chaque option, exécute
l’option (potentiellement -L
pour ajouter des dossiers à
load-path
) et se positionne sur le bon buffer, ou affiche le
startup screen
.
Avant d’ajouter des choses dans votre .emacs
, il faut donc vérifier
que ce n’est pas déjà ajouté dans site-run-file
.
Le fichier init
Je traduis ici très grossièrement les parties intéressantes du manuel (Init-File (manuel emacs), manuel que je vous encourage à lire).
Quand Emacs démarre, il essaie de charger un fichier d’initialisation
qui dit comment l’utilisateur souhaite que démarre Emacs (suivant
l’utilisateur, voir Find-Init (manuel emacs)). Emacs cherche le
fichier init parmi ~/.emacs
, ~/.emacs.el
, ou ~/.emacs.d/init.el
.
Il peut également y avoir un fichier init par défaut, qui est la
bibliothèque default.el
, trouvé dans le load-path
. Si elle existe,
elle est chargée à chaque démarrage d’Emacs (sauf si -q
), mais le
fichier init est chargé en premier. S’il met inhibit-default-init
à
non-nil, default
n’est pas chargé.
Il peut y a voir aussi un fichier de démarrage de « site »,
site-start.el
. Il est chargé avant le fichier init, sauf si
--no-site-file
.
Les fichiers default.el
et site-start.el
peuvent être dans
n’importe quel dossier de la variable load-path
(souvent dans un
dossier site-lisp
comme : /usr/local/share/emacs/site-lisp
).
Pour voir le load-path
de votre Emacs en cours : C-h v load-path.
Pour connaître votre fichier init, et donc savoir quel fichier
~/.emacs(.d/init)?.elc?
a été chargé, C-h v user-init-file.
En bref
Au démarrage, il se passe tout un tas de choses, dont le chargement de
plein de fichiers2. Je vais résumé ici les fichiers chargés (et
les fonctions lancées) qui peuvent avoir un impact sur load-path
, et
sur lesquels l’utilisateur peut avoir une influence.
Le load-path
initial contient quelque chose comme :
/.../emacs24/share/emacs/24.3.50/site-lisp /.../emacs24/share/emacs/site-lisp /.../emacs24/share/emacs/24.3.50/lisp
avant même qu’Emacs ne soit lancé.
Puis sont ajoutés les sous-dossiers de ces dossiers (qui sont dans les
subdirs.el
respectifs) récursivement.
Ensuite, Emacs :
- lance
before-init-hook
; - charge le fichier
site-run-file
(si présent et si pas-no-site-file
) qui est commun à tous les utilisateurs et que seul l’admin du site devrait toucher (normalementsite-start.el
) ; - charge le fichier init de l’utilisateur (si pas
-q
), qui peut être~/.emacs
,~/.emacs.el
, ou~/.emacs.d/init.el
; - charge le fichier
default
(normalementdefault.el
) sauf si-q
ou siinhibit-default-init
n’est pas nil ; - lance
after-init-hook
; - ajoute les dossiers passé en argument à l’option
-L
ou-directory
.
Concernant le chargement des fichiers : par défaut (variable
auto-compression-mode
), il cherche la version compressée (.gz
,
jka-compr-load-suffixes
), et si ce n’est pas précisé autrement lors
de l’appel de load
, il cherche avec les extensions .el
, .elc
et
sans extension.
Savoir comment charger une fonction/un fichier.
On va voir ici ce que fait la fonction autoload
et si c’est bien
elle qu’il faut appeler.
Rien de mieux qu’une traduction approximative du manuel et de l’aide pour connaître nos options. Si vous lisez l’anglais, allez donc plutôt voir Loading (manuel elisp), puis allez au résumé.
How Programs Do Loading: The load function and others.
How-Programs-Do-Loading (manuel elisp)
- Function:
load filename &optional missing-ok nomessage nosuffix must-suffix
:Ouvre un fichier lisp, évalue, ferme le fichier.
Cherche d’abord
filename.elc
. Si existe, est chargé. Sinon, cherchefilename.el
. Si existe, est chargé. Sinon, cherchefilename
et charge si existe. (Attention àfoo.el.el
:(load "foo.el")
marche…)Si
auto-compression-mode
est activé (cas par défaut) cherche la version compressée si le fichier n’est pas trouvé, puis passe aux suivants. Décompresse et charge si existe (ajoute chacun des suffixes dansjka-compr-load-suffixes
).Si
nosuffix non-nil
, n’essaie pas d’ajouter.el/.elc
Si
must-suffix non-nil
,load
insiste sur le fait que le nom du fichier doit finir par.el/.elc
.Si
filename
est un nom de fichier relatif (foo ou baz/foo.bar),load
cherche le fichier par la variableload-path
. Il charge le premier fichier possible. Sinil
dansload-path
, essaie le dossier courant. Les trois premiers fichiers dans le premier dossier deload-path
sont tentés, puis les trois du second dossier, etc.Peu importe où Emacs trouve le fichier (nom/dossier) la variable
load-file-name
sera valorisée à ce nom de fichier.Des messages comme
‘Loading foo...’
et‘Loading foo...done’
apparaissent, sauf sinomessage
estnon-nil
.Si
load
ne trouve pas le fichier, normalement, il signale l’erreurfile-error
. Mais simissing-ok non-nil
, alorsload
retourne seulementnil
.load
retournet
si le fichier est chargé avec succès.- Donc
load nomfichier [retourneNilSiAbsent?SinonErreur nonVerbeux? tenteDAjouter.el/.elc? doitFinirPar.el/.elc?]
Du coup, on utilisera le plus souvent
(load "chose")
ou(load "chose.el" t t t)
(avec des variantes sur missing-ok et nomessage).C-h f load : Charger un fichier enregistre ses définitions, ses appels
provide
etrequire
dansload-history
. La variableload-in-progress
est non-nil pendant le chargement,load-file-name
contientle nom du fichier.
- Donc
- Command:
load-file filename
:Charge le fichier
filename
. Sifilemane
est un nom relatif, le dossier courant est présumé. N’utilise pasload-path
, n’utilise pas de suffixes, mais cherche une version compressée.- En très gros, fait
(load (expand-file-name file) nil nil t))
.
- En très gros, fait
- Command:
load-library library
:Cette commande charge la bibliothèque
library
. Commeload
, sauf que lit son argument de façon interactive.- En gros, fait
(load library)
.
- En gros, fait
Load Suffixes: Details about the suffixes that load tries.
Quels suffixes sont tentés (.el
, etc.): voir point précédent.
Library Search: Finding a library to load.
Quand Emacs cherche une bibliothèque, il cherche dans load-path
qui contient des chaînes de caractères correspondant à des dossiers
ou nil
pour le dossier courant.
À chaque démarrage, il initialise load-path
par la variable
d’environnement EMACSLOADPATH
. Si elle n’est pas défini, Emacs
initialise load-path
avec
/usr/local/share/emacs/version/site-lisp
et
/usr/local/share/emacs/site-lisp
et ajoute à load-path
les
sous-dossiers récursivement (sauf si ne commence pas par
chiffre/lettre, vaut CVS
ou RCS
, contient un fichier
.nosearch
). Puis, comme vu précédemment, ajoute ceux indiqués par
-L
, et ceux du fichier init de l’utilisateur.
- Command:
locate-library library &optional nosuffix path interactive-call
:Trouve le vrai nom de la bibliothèque
library
. Cherche comme le faitload
, l’argumentnosuffix
ayant le même sens : ne pas ajouter.elc
ou.el
au nom.Si
path
est non-nil, cette liste de dossier est utilisée au lieu deload-path
.Si la commande est appelé depuis un programme, retourne le nom en tant que
string
. Si appelé avec M-x l’argumentinteractive-call
estt
, et le résultat est affichée dans la zone d’affichage des messages (echo area). - Command:
list-load-path-shadows &optional stringp
:Cette commande montre une liste de fichiers Emacs lisp « occultés » (shadowed). Un fichier occulté est un fichier qui ne sera normalement pas chargé alors qu’il est dans un dossier dans
load-path
, mais parce qu’un autre fichier au nom similaire se situe dans un dossier deload-path
.Par exemple, si
load-path
vaut("/opt/emacs/site-lisp" "/usr/share/emacs/23.3/lisp")
et que dans les deux dossiers un fichierfoo.el
existe, alors(require 'foo)
ne chargera jamais le fichier du second dossier. Une telle situation peut indiquer un problème dans la façon dont Emacs a été installé.Quand appelée depuis Lisp, cette fonction affiche un message contenant la liste des fichiers occultés, plutôt que de les afficher dans un buffer. Si l’argument
stringp
est non-nil, elle retourne les fichiers occultés dans unstring
.
Loading Non-ASCII: Non-ASCII characters in Emacs Lisp files.
Pas pertinent pour le sujet.
Autoload: Setting up a function to autoload.
autoload
permet d’enregistrer l’existence d’une fonction ou macro,
et repousse le chargement du fichier qui la définit. Le premier
appel à la fonction charge automatiquement la bonne bibliothèque,
pour installer la vraie définition (et d’autres choses associées),
puis exécute la définition comme si cela avait déjà été chargé.
L’autoloading peut également être déclanché en regardant la doc
d’une fonction (C-h f).
Il y a deux façons de faire une fonction « autoloaded » : en appelant
autoload
, et en écrivant un commentaire « magique » avant la vraie
définition. autoload
est la primitive bas niveau pour
l’autochargement ; n’importe quel programme Lisp peut appeler
autoload
à n’importe quel moment. Les commentaires magiques sont
le moyen le plus pratique de faire « autoloader » une fonction, pour
les paquets installés avec Emacs. Ces commentaires ne font rien en
eux-même, mais ils servent de guide pour la commande
update-file-autoloads
, qui construit les appels à autoload
et
fait en sorte de les exécuter quand Emacs est construit.
- Function:
autoload function filename &optional docstring interactive type
Cette fonction définit la fonction (ou macro)
function
pour pouvoir charger automatiquement à partir du fichierfilename
. La chaînefilename
spécifie le fichier à charger pour obtenir la vraie définition de la fonction.Si
filename
ne contient ni un nom de dossier, ni les suffixes .el ou .elc, cette fonction ajoute un de ces suffixes, et ne chargera pas à partir d’un fichier dont le nom est justefilename
sans suffixe. (La variableload-suffixes
spécifie les suffixes nécessaires.)L’argument
docstring
est la chaîne contenant la doc de la fonction. Mettre cette documentation permet de la regarder sans charger la vraie définition. Normalement, elle devrait être identique à celle de la définition de la fonction. Si ce n’est pas le cas, la doc de la définition de la fonction prendra effet quand elle sera chargée.Si
interactive
est non-nil, la fonction peut être appelée interactivement. Cela permet à la complétion dans M-x de marcher sans charger la vraie définition de la fonction. La vraie façon dont la fonction est interactive est inutile, sauf si l’utilisateur appelle vraiment la fonction, et dans ce cas la vraie définition est chargée.Il est possible d’autoloader les macros et raccourcis clavier comme les fonctions ordinaires. Mettre
macro
danstype
si la fonction est en fait une macro,keymap
si c’est un raccourci clavier.Si
function
a déjà une définition qui est non vide et qui n’est pas un objet autoload, la fonction ne fait rien et retournenil
. Sinon, elle construit un objet autoload, et le met en tant que définition de la fonctionfunction
. - Function:
autoloadp object
Cette fonction retourne non-nil si
object
est un objet autoloadé.Le fichier autoloadé contient généralement d’autres définitions et peut faire des
require
ouprovide
. Si le fichier n’est pas complétement chargé (à cause d’une erreur dans l’évaluation de son contenu), toutes les définitions des fonctions etprovide
qui sont apparus dans le chargement sont défaits. Ceci est fait pour s’assurer que la prochaine tentative d’appel à une fonction autochargée de ce fichier essayera à nouveau de charger le fichier pour être sûr que les fonctions marchent.Si le fichier autochargé échoue à définir la fonction ou macro Lisp désirée, alors une erreur est signalée « Autoloading failed to define function function-name ».
(La suite concerne moins le sujet, mais c’est intéressant, donc je mets pour me souvenir.)
Un commentaire magique
autoload
(souvent appeléautoload cookie
) est composé de;;;###autoload
, sur une ligne, tout seul, juste avant la vraie définition de la fonction. La commande M-x update-file-autoloads écrit l’appel autoload correspondant dansloaddefs.el
. (La chaîne commentaire et le fichier peuvent être changés, voir plus bas.) Construire Emacs chargeloaddefs.el
et appelleautoload
. M-x update-directory-autoloads est encore plus puissant ; ça met à jour les autoloads pour tous les fichiers dans le dossier courant.Le même commentaire magique peut copier n’importe quel type de form dans
loaddefs.el
. La form qui suit le commentaire magique est copié tel quel, sauf si c’est une des forms que autoload gère différemment (par exemple en convertissant en appelautoload
) :Les définitions des objets fonction :
defun
etdefmacro
[comme on vient de voir pour ceux qui suivent] ; et aussicl-defun
,cl-defmacro
etdefine-overloadable-function
.Les définitions des major/minor modes :
define-minor-mode
,define-globalized-minor-mode
,define-generic-mode
,define-derived-mode
,easy-mmode-define-minor-mode
,easy-mmode-define-global-mode
,define-compilation-mode
, etdefine-global-minor-mode
.D’autres types de définition :
defcustom
,defgroup
,defclass
, etdefine-skeleton
.On peut aussi utiliser un commentaire magique pour exécuter une form au moment de la construction sans l’exécuter quand le fichier lui-même est chargé. Pour ce faire, écrire la form sur la même ligne que le commentaire magique. Puisque c’est un commentaire, ça ne fait rien quand on charge le fichier source ; mais M-x update-file-autoloads le copie dans
loaddefs.el
, où il sera exécuté lors de la construction d’Emacs.Un exemple de fonction avec le commentaire magique :
;;;###autoload (defun doctor () "Switch to *doctor* buffer and start giving psychotherapy." (interactive) (switch-to-buffer "*doctor*") (doctor-mode))
Ce qui est écrit dans loaddefs.el
:
(autoload (quote doctor) "doctor" "\ Switch to *doctor* buffer and start giving psychotherapy. \(fn)" t nil)
Si on a écrit une définition de fonction avec une macro qui n’est
pas celle utilisée d’habitude (par exemple mydefunmacro
),
l’utilisation d’un commentaire magique normal copierait toute la
définition dans loaddefs.el
(puisque ce n’est pas une des
exceptions vues précédemment). Ce n’est pas ce que l’on souhaite.
Pour mettre le bon appel autoload
dans loaddefs.el
, il faut
plutôt écrire :
;;;###autoload (autoload 'foo "myfile") (mydefunmacro foo ...)
Comme dit précédemment, on peut utiliser une chaîne différente
pour le cookie autoload et quand même avoir l’appel autoload
correspondant écrit dans un fichier différent de loaddefs.el
.
- Variable:
generate-autoload-cookie
La valeur de cette variable doit être une chaîne dont la syntaxe est une commande Lisp. M-x update-file-autoloads copie la form Lisp qui suit le cookie dans le fichier autoload qu’il génère.
- Variable:
generated-autoload-file
La valeur de cette variable nomme un fichier Emacs Lisp où les appels
autoload
doivent aller (par défautloaddefs.el
)Pour charger explicitement la bibliothèque spécifiée par un objet autoload, on peut utiliser la fonction :
- Function:
autoload-do-load autoload &optional name macro-only
Cette fonction effectue le chargement spécifié par
autoload
, que devrait être un objet autoload.
Repeated Loading: Precautions about loading a file twice.
Comme le dit le titre. Intéressant, mais pas pour notre problème.
Named Features: Loading a library if it isn't already loaded.
provide
et require
sont une autre façon de charger
automatiquement des fichiers. Le chargement automatique est
déclanchée en appelant une fonction particulière, mais une
feature est chargée la première fois qu’un autre programme la
demande, par son nom.
Un nom de feature est un symbole qui représente une collection de
fonctions, variables, etc. Le fichier qui les définit doit faire un
provide
de la feature
.
Un autre programme qui les utilise peut s’assurer qu’elles sont
définies en faisant un require
de la feature
. Cela charge le
fichier de définitions s’il n’était pas déjà chargé (en regardant
la variable globale features
). Si le fichier n’a pas d’appel
« haut niveau » à provide
, require
signale une erreur.
Where Defined: Finding which file defined a certain symbol.
Avec la fonction symbol-file
(qui cherche dans load-history
).
(Par exemple, évaluez (C-x e derrière la parenthèse fermante)
dans un buffer (symbol-file 'emacs-lisp-mode)
ou (symbol-file
'split-window)
.)
Unloading: How to "unload" a library that was loaded.
unload-feature feature
(qui s’aide lui aussi de load-history
)
« décharge » la librairie qui fait un provide feature
. Ça enlève
les définitions des fonctions, macros… crées par des defun
,
defalias
, defsubst
, defmacro
, et restaure les autoloads.
Hooks for Loading: Providing code to be run when particular libraries are loaded.
On peut demander à ce que du code soit exécuté chaque fois qu’Emacs
charge une bibliothèque, en utilisant la variable
after-load-functions
:
— Variable: after-load-functions
Ce hook est lancé après le chargement d’un fichier. Chaque fonction dans le hook est appelé avec un seul argument, le nom avec chemin absolu du fichier qui vient d’être chargé.
Si on veut que du code soit exécuté quand une bibliothèque en
particulier est chargée, il faut utiliser eval-after-load
:
— Function: eval-after-load library form
Cette fonction évalue form
à la fin du chargement du fichier
library
, chaque fois que library
est chargée. Si elle est déjà
chargée, elle évalue form
immédiatement. Par exemple :
(eval-after-load "edebug" '(def-edebug-spec c-point t))
Normalement, les programmes Lisp bien faits ne devraient pas
utiliser eval-after-load
. Si on a besoin d’examiner et
d’initialiser des variables définies dans une autre bibliothèque
(celles définies pour un usage extérieur à la bibliothèque), on
peut le faire immédiatement. Il n’y a pas besoin d’attendre qu’une
bibliothèque soit chargée. Si on a besoin d’appeler des fonctions
définies dans cette bibliothèque, il faut charger la bibliothèque
(de préférence avec require
).
— Variable: after-load-alist
Contient une alist construite par eval-after-load
, contenant les
expressions à évaluer quand certaines bibliothèques sont chargées.
En bref
Pour charger un fichier (et évaluer ce qu’il y a dedans), on utilise
load
. Mais, pour dire à emacs « Hey, la fonction foo
est dans le
fichier bar.el
, mais tu charges (c’est-à-dire évalues) le fichier
qui contient la fonction que quand t’en as vraiment besoin. », et
ainsi pouvoir utiliser foo
sans charger le fichier, on utilise
autoload
. Par exemple,
(autoload 'foo "bar" "Some docstring." t)
dans le .emacs
, va dire que la fonction php-mode
se trouve dans un
fichier php-mode
(avec extension .el
ou .elc
).
De plus, si dans un fichier « standard » il y a des lignes
;;;###autoload
, alors pas besoin d’exécuter des (autoload ...)
, le
fichier loaddefs.el
contient certainement l’appel qui va bien.
Si par contre c’est votre fichier, et que vous voulez ajouter les
fonctions qui suivent les ;;;###autoload
à loaddefs.el
, il faut
appeler update-file-autoloads
(ou update-directory-autoloads
).
Comment est choisi le mode d’un fichier
Choosing-Modes (manuel emacs) Auto-Major-Mode (manuel elisp)
Quand on ouvre un fichier, avec find-file fichier
ou recover-file
,
les fonctions appellent normal-mode fichier
(dans files.el
).
normal-mode fichier
appelle fundamental-mode
sur le buffer puis
appelle set-auto-mode
.
Quand on regarde C-h f set-auto-mode :
La fonction cherche un mode de plusieurs façons :
- Elle cherche un
-*-foo-mode-*-
(voir C-h f set-auto-mode-1) dans le buffer. - Si pas trouvé, cherche un
#!/
à la première ligne (en fait C-h v auto-mode-interpreter-regexp pour être exact). - Si pas trouvé, cherche une regexp pour la première ligne du buffer
(dans C-h v magic-mode-alist (défaut à vide)). Par exemple
("<\\?xml " . xml-mode)
dit que si la première ligne commence par<\\?xml
, on passe enxml-mode
. - Si pas trouvé, cherche une regexp sur le nom de fichier, souvent
l’extension (dans C-h v auto-mode-alist (case-sensitive, puis
insensitive)). Par exemple
("\\.svgz?\\'" . xml-mode)
dit que si le fichier se termine par.svg
ou.svgz
, on passe enxml-mode
. - Si pas trouvé, cherche une regexp pour la première ligne du buffer (dans C-h v magic-fallback-mode-alist).
- Si toujours pas trouvé, on appelle
set-buffer-major-mode
qui dit que :- si le buffer est
*scratch*
,initial-major-mode
; - sinon
major-mode
(c’est-à-direfundamental-mode
par défaut).
- si le buffer est
La plupart du temps, une ligne (add-to-list 'auto-mode-alist
'("\\.[ch]\\'" . c-mode))
sera présente dans le fichier (précédée ou
non d’un commentaire magique).
Bonus
Specifying-File-Variable (manuel emacs)
Le -*-
ne sert en fait pas qu’à mettre le mode. On peut aussi mettre
des variables spécialement pour un fichier. (Pour tous les fichiers
d’un dossier, c’est possible aussi, avec un fichier .dir-locals.el
Directory-Variables (manuel emacs).)
Par exemple,
;; -*- mode: Lisp; fill-column: 75; comment-column: 50; -*-
mettra en mode Lisp et instanciera les deux variables. (Plutôt que d’éditer à la main, on peut utiliser M-x add-file-local-variable-prop-line).
Normalement, c’est sur la première ligne, mais pour les scripts shell (entre autres) où la première ligne sert à connaître l’interpréteur, il peut être sur la seconde ligne.
Ces variables peuvent également être définies à la fin du fichier (avec M-x add-file-local-variable par exemple) sous forme de liste :
/* Local Variables: */ /* mode: c */ /* comment-column: 0 */ /* End: */
La liste doit commencer par Local Variables:
et se terminer par
End:
. Emacs reconnaît automatiquement le préfixe (et comme ici,
éventuellement suffixe) en cherchant autour Local Variables:
.
Pour activer un mode mineur, il faut utiliser eval:
:
;; Local Variables: ;; eval: (eldoc-mode) ;; eval: (font-lock-mode -1) ;; End:
Comme ça peut être dangereux, Emacs peut demander si on veut vraiment les utiliser (Safe-File-Variables (manuel emacs), C-h v safe-local-variable-values).
Hooks
Les hooks (manuel elisp) sont des variables où l’on peut stocker une ou des fonctions à appeler pour une occasion particulière. Emacs permet ainsi de modifier le comportement de certaines fonctions.
Après avoir chargé un fichier, le mode est choisi, puis appelé. Pour
ajouter des fonctions ou appeler des modes mineurs sur ces fichiers,
on peut donc utiliser les hooks. Généralement pour chaque mode, il
existe un hook qui est exécuté après le chargement du mode : pour
nomdumode-mode
il y a nomdumode-mode-hook
. La dernière chose qu’un
major-mode
fait, c’est appeler run-mode-hooks
. Cette fonction
lance change-major-mode-after-body-hook
, nomdumode-mode-hook
puis
after-change-major-mode-hook
.
Si un mode dérive d’un autre, le -mode-hook
du parent est d’abord
exécuté. Du coup, pour changer tous les modes de programmation, il
suffit d’ajouter une fonction à prog-mode-hook
.
Setting-Hooks (manuel elisp)
Un exemple qui utilise le mode hook pour mettre passer en Auto Fill
mode
quand on passe en mode Lisp Interaction
:
(add-hook 'lisp-interaction-mode-hook 'auto-fill-mode)
Normalement, la fonction que l’on ajoute au hook ne prend pas d’argument, mais il est possible de contourner cela avec les fonctions lambda (ModeHooks (emacswiki)) :
(add-hook 'text-mode-hook (lambda () (set-fill-column 72))) (add-hook 'text-mode-hook (lambda () (column-number-mode 1)))
Hooks-for-Loading (manuel elisp)
(Je reprends ce que je dis plus haut, en ajoutant des exemples.)
On peut également demander à ce que du code soit exécuté à chaque fois
qu’Emacs charge une bibliothèque avec le hook after-load-functions
(attention, chaque fonction du hook est appelée avec un argument : le
chemin complet du fichier qui vient d’être chargé).
Si on veut que le code soit exécuté à chaque fois qu’une bibliothèque
est chargée, il faut utiliser eval-after-load
. La fonction évalue le
code à la fin de chaque chargement de la bibliothèque. Si elle est
déjà chargée, le code est exécuté immédiatement.
Par exemple, pour charger la bibliothèque my-foo-config
après que
file-foo.txt
ait été chargé :
(eval-after-load "file-foo.txt" '(load "my-foo-config"))
Pour exécuter la fonction foo-bar
après avoir chargé le mode
foobar
:
(eval-after-load "foobar-mode" '(foo-bar"))
Différences
On peut parfois utiliser indifférement l’une ou l’autre des méthodes.
Mais, le code de eval-after-load
sera exécuté seulement une fois, et
donc est généralement utilisé pour par exemple instancier des valeurs
globales par défaut ou configurer un keymap par défaut pour un certain
mode. Dans le code du eval-after-load
, il n’y a pas non plus de
notion de buffer courant.
Les nomdumode-mode-hooks
, eux, sont exécutés pour chaque buffer dans
lequel le mode sera activé.
Conclusion
Que fait le code du début
Reprenons notre bout de code du début :
(autoload 'php-mode "php-mode" "Major mode for editing php code." t) (add-to-list 'auto-mode-alist '("\\.php$" . php-mode))
La première ligne indique donc à Emacs où chercher la fonction
php-mode
– dans un fichier php-mode.el
(ou .elc
) – mais que pour
le moment, ce n’est pas la peine de charger et d’évaluer le fichier
(et donc la fonction).
La deuxième indique que quand Emacs va ouvrir un fichier, s’il n’a pas
trouvé de ligne contenant -*-foo-mode-*-
, de shebang (#!
)
indiquant quel mode charger, et que le fichier se termine par .php
,
alors, il faut lancer la fonction php-mode
.
Ces lignes permettent donc de (et sont nécessaire pour) passer en
php-mode
quand on ouvre un fichier dont l’extension est .php
sans
forcément charger le fichier php-mode.el
.
C’est plutôt pas mal, sauf que, ces lignes sont sans doute totalement
inutiles… Pour s’en rendre compte, il suffit de lancer Emacs sans
fichier de config avec -q
ou --no-init-file
(voire -Q
qui fait
--no-init-file --no-site-file --no-splash
), et de regarder si le
fichier qu’on veut ouvrir se met bien dans le bon mode. Ou alors, de
regarder auto-mode-alist
, ainsi que loaddefs.el
.
Comment configurer un mode
En utilisant les hooks. La façon la plus simple est d’utiliser :
(add-hook 'text-mode-hook 'auto-fill-mode)
Si on veut utiliser une fonction avec argument, il faut passer par des fonctions lambda :
(add-hook 'latex-mode-hook (lambda () (auto-fill-mode -1)))
Voilà, je ne sais pas si c’est plus clair pour vous, mais en tout cas,
ça m’a permis d’en apprendre un peu plus sur Emacs. (Et encore, je ne
vous ai pas parlé de hack-local-variables-hook
, du fichier
.dir-locals.el (manuel emacs), de…
(Merci aux relecteurs.)
Footnotes:
Voir par exemple cette configuration
Rien ne sert de modifier startup.el
, le fichier sert
« seulement » à construire les exécutables Emacs DumpingEmacs
(emacswiki). Il est chargé par loadup.el
(qui est en fait la
première chose chargée (source c)) après plusieurs autres
bibliothèques comme :
subr
: qui contient des fonctions lisp commepush
,caar
,last
,add-hook
,with-temp-buffer
,save-window-excursion
, etc.) ;custom
: qui contient le code nécessaire à la déclaration et l’initialisation des options via customize ;window
: qui touche auxwindows
etframes
(delete-other-window
,split-window
,switch-to-buffer
, etc.) ;files
: ce qui touche aux fichiers (find-file
,recover-file
,save-buffer
,write-file
, etc.) ;
C’est un peu compliqué de savoir comment est définit load-path (source c) à la compilation d’Emacs (comme ça dépend de comment ça a été compilé) et donc connaître les additions des répertoires, mais comme les (sous-)dossiers sont ajoutés à la fin, on peut retrouver sa valeur au lancement d’Emacs.