Emacs: Yasnippet
Hello,
Au menu aujourd'hui: Yasnippet, un système de gabarit pour Emacs.
Son principe est simple: Sauvegarder des extraits de texte, aussi appelés gabarits, pour les ré-utiliser plus tard. Chaque gabarit est associé à un mot-clé. Et à chaque fois où tu as besoin d'un gabarit, tu écris juste son mot-clé et il sera remplacé par le texte qui lui associé.
Avec Yasnippet:
- On peut définir des gabarits, avec un éditeur
- Chaque gabarit est associé au mode Emacs où il est utile
- Pour chaque gabarit, il est possible de définir des champs
Ces champs:
- Sont complétés au moment où le texte du gabarit est inséré
- Peuvent avoir des valeurs par défaut
- Peuvent avoir, comme valeur, le résultat d'une fonction
Prérequis
Connaissances
Pour comprendre cette article, il faut avoir quelques notions de base avec Emacs. Savoir comment appeler une fonction interactive et ce qu'est un mode sera suffisant.
Il faut aussi connaitre le format suivant pour les raccourcis clavier:
C-i
: Correspond à [CTRL] + [i]M-x
: Correspond à [Alt] + [x]S-i
: Correspond à [Shift] + [i]C-c c
: Correspond à [Ctrl] + [c], relâcher, puis [c]C-c C-e
: Correspond à [Ctrl] + [c], relâcher, puis [Ctrl] + [e]Tab
: Correspond à [Tabulation]
Logiciels
Pour utiliser Yasnippet, il faut :
Pour Emacs, si tu utilise une distribution GNU/Linux, il est certainement disponible sur les dépôts de ta distribution. Si tu utilise Mac OS ou Windows, je te renvoie au site web officiel d'Emacs.
Pour Yasnippet, j'explique dans la section Installation de Yasnippet comment l'installer.
Préparations
TL;DR
Si les détails ne t'intéressent pas, voici comment préparer Yasnippet en 4 étapes:
- Depuis Emacs, appeler le raccourcis clavier
M-x package-refresh-content
- Appeler ensuite
M-x package-install
et indiqueryasnippet
- Ajouter ce morceau de code
Elisp
dans le fichier~/.emacs.d/init.el
:
(require 'yasnippet) (yas-global-mode 1)
- Évaluer le morceau de code
Elisp
Alternativement, si tu as use-package
, 2 étapes suffisent:
- Ajouter ce morceau de code
Elisp
dans le fichier~/.emacs.d/init.el
:
(use-package yasnippet :config (require 'yasnippet) (yas-global-mode 1))
- Évaluer le morceau de code
Elisp
Installation de Yasnippet
Yasnippet est disponible sur le dépôt ELPA
, que Emacs connait par
défaut. Il faut juste dire à Emacs d'installer Yasnippet.
Pour ça, 2 possibilités. Au choix: Manuellement ou automatiquement.
Manuellement
Depuis Emacs, lance la fonction package-refresh-content
, avec le
raccourcis clavier M-x
suivit du nom de la fonction. Emacs vas
récupérer auprès des dépôts les paquets disponibles.
Ensuite, lance la fonction package-install
, toujours avec le
raccourcis M-x
. Emacs demande quel paquet installer. Réponds
simplement yasnippet
.
Et voilà, Yasnippet est installé.
Automatiquement
Si tu as l'habitude de configurer Emacs avec le fichier
~/.emacs.d/init.el
, et que le paquet use-package
est installé, tu
peux ajouter ceci dans ~/.emacs.d/init.el
:
(use-package yasnippet)
Une fois cet instruction Elisp
évaluée, Yasnippet est installé.
Note: use-package
est installé par défaut à partir d'Emacs 29.1.
Activer un des 2 modes Yasnippet
Yasnippet est utilisable en activant l'un de ces 2 modes mineur:
yas-global-mode
yas-minor-mode
Un seul des deux modes est nécessaire.
Pour cet article, on va utiliser le mode globale car il est accessible
depuis chaque buffer Emacs. Pour l'utiliser, il suffit d'ajouter ces
instructions Elisp
dans le fichier ~/.emacs.d/init.el
:
(require 'yasnippet) (yas-global-mode 1)
Une fois ces instructions évaluées, Yasnippet peut être utilisé.
Pour information, la première instruction charge Yasnippet et la seconde active le mode globale de Yasnippet.
Alternativement, si tu as appelé use-package
pour l'installation, tu
peux lier l'activation du mode à la présence de Yasnippet. Pour ça,
modifie l'instruction d'installation comme ceci:
(use-package yasnippet :config (require 'yasnippet) (yas-global-mode 1))
Le deuxième mode, yas-minor-mode
ne sera actif que dans le buffer où
tu l'appelle. Il s'utilise comme n'importe quel mode mineur. Mais je
ne vais pas rentrer plus en détails sur son activation. Pour
commencer, le mode global est suffisant.
Un premier gabarit
On va prendre un premier exemple simple: Une signature.
Imagine que tu souhaite ajouter la signature suivante sur un texte:
Avec mes plus sincères salutations, ----- Fox
On va créer un gabarit à partir de cette signature.
Création du gabarit
Pour créer ce gabarit, il faut:
- Ouvrir, avec Emacs, un texte où tu souhaite utiliser ta signature pour la première fois
- Écrire ta signature là où tu le souhaite
- Sélectionner cette signature et appeler la fonction
yas-new-snippet
(avec le raccourcisM-x
)
Là, une nouvelle fenêtre s'ouvre et tu peux y lire ceci:
# -*- mode: snippet -*- # name: # key: # -- Avec mes plus sincères salutations, ----- Fox
Il s'agit de ton nouveau gabarit, pas encore sauvegardé.
On peut y voir un en-tête, avec 2 champs, name
et key
, ainsi que
la signature que tu avais sélectionné. Le champ name
sera le nom du
gabarit et key
le mot-clé qui lui sera associé.
Ton curseur d'écriture se trouve juste après le champ name
, il te
suffit d'écrire le nom du gabarit. Au fur et à mesure que tu l'écris,
le champ key
se complétera tout seul.
Après avoir écrit le nom du gabarit, appuie sur la touche Tab
de ton
clavier. Ton curseur d'écriture va sauter au niveau du champ key
. Tu
pourra ainsi corriger le mot-clé, si tu en veux une autre que celui
proposé.
Une fois le mot-clé défini, appuie à nouveau sur la touche
Tab
et ton curseur va sauter au début du texte de ton
gabarit. Tu peux maintenant l'ajuster, si nécessaire.
Activer/sauvegarder le gabarit
Pour Activer/sauvegarder ton gabarit, il faut appeler le raccourcis
clavier C-c C-c
.
Emacs te demandera:
- Pour quel mode utiliser ton gabarit
- Si tu souhaite le sauvegarder
- Si oui pour la sauvegarde, où tu souhaite le sauvegarder
Pour le chemin de sauvegarde, il est important de garder le dossier
~/.emacs.d/snippet/ton-mode/
pour le moment. ton-mode
correspondant au mode pour lequel tu veux utiliser ce gabarit. Voir la
section Définir les dossiers de gabarit pour plus de détails.
Quand un gabarit est actif, voir sauvegardé, il le sera pour un seul
mode. Ainsi, par exemple, les gabarits définit pour Org-mode
ne
seront accessible que depuis un buffer avec le mode Org-mode
actif.
Ceci afin que pour chaque mode Emacs, tu n'ai accès qu'aux gabarits
qui sont pertinent. Dans la section Utiliser des gabarits d'un mode à
l'autre, j'explique comment ré-utiliser pour un mode les gabarits d'un
autre mode.
Utilisation du gabarit
Maintenant, depuis un texte où tu souhaite utiliser ta signature, il suffit d':
- Écrire le mot-clé associé à la signature
- Appuyer sur la touche du clavier
Tab
Et voilà, le mot-clé est remplacé par ta signature.
Une autre façon d'insérer un gabarit est d'appeler la fonction
yas-insert-snippet
, avec la raccourcis M-x
, puis d'indiquer le
gabarit à insérer.
Définir et utiliser des champs
Pour le moment, on a définit un gabarit avec un texte statique. Mais peut-être qu'on veut définir dynamiquement certaines parties du texte.
Prenons l'exemple de texte suivant: Des notes de lecture pour un livre,
géré avec Org-mode
.
Voici à quoi le texte ressemblerait:
* Titre du livre :book:read_notes: :PROPERTIES: :Author: Nom de l'auteur ou autrice :Editor: Nom de la société d'édition :Collection: Nom de la collection :END:
Ici, on a:
- Une section Org-mode, dont le titre est le titre du livre
- Les noms de l'auteur/autrice, de société d'édition et de collection, sous la forme de propriétés
- 2 étiquettes:
book
etread_notes
Les notes de lectures pourront ensuite être ajoutées sous la forme de sous-sections.
Maintenant, si je transforme ce texte en gabarit, j'aimerais bien qu'au moment où je l'utilise Emacs me demande les valeurs pour le titre, l'auteur/autrice, la collection, etc.
Voici comment faire.
Définir des champs
Au moment de créer un nouveau gabarit, on peut déclarer des champs dans son texte en utilisant la syntaxe suivante:
$n
, où n
est le numéro du champ.
Si je reprends l'exemple des notes de lectures, voici son gabarit:
# -*- mode: snippet -*- # name: Read notes # key: read_notes # -- * $1 :$5:read_notes: :PROPERTIES: :Author: $2 :Editor: $3 :Collection: $4 :END: $0
Comme tu peux le voir, j'ai définit 5 champs:
- Titre du livre, champs numéro 1 (
$1
) - Nom de l'auteur/autrice, champs numéro 2 (
$2
) - Nom de la société d'édition, champs numéro 3 (
$3
) - Nom de la collection, champs numéro 4 (
$4
) - Étiquette du type d'ouvrage, champs numéro 5 (
$5
)
J'ai également définit un 6ème champ, $0
. C'est là où ton curseur
d'écriture sera positionné après que le texte du gabarit soit inséré
et complété.
Utiliser des champs
Maintenant, si tu sauvegarde le gabarit et l'utilise:
- Le texte du gabarit sera inséré sans les
$n
- Ton curseur d'écriture sera placé au niveau du
$1
: Tu peux écrire le titre du livre - Si tu appuie sur la touche
Tab
, ton curseur sautera à l'emplacement du$2
- Chaque saut du curseur va suivre l'ordre des champs, du plus petit au plus grand
- Pour revenir à un champ précédent, appui sur le raccourcis
S-Tab
([Shift] + [Tabulation]
) - Après être arrivé au dernier champ (
$5
), un nouvel appui surTab
stoppera le remplissage des champs et déplacera le curseur sur l'emplacement de$0
Valeurs par défaut
Définir des champs, c'est pratique. Mais ça l'est encore plus si on peut définir des valeurs par défaut.
Pour ça, quand tu déclare un champs dans un gabarit, utilise la syntaxe suivante:
${n:Valeur par défaut}
Remplace n
par le numéro du champ et Valeur par défaut
par ce que
tu veux.
Maintenant, quand tu utilisera ton gabarit, tes champs auront une valeur par défaut. Au moment de compléter un champ, si tu écrit quelque chose, la valeur par défaut est effacée et remplacée par ce que tu écris. Si tu n'écris rien et saute au champs suivant, la valeur par défaut est préservée.
Reprendre une valeur d'un champ à l'autre
Si tu définis 2 champs avec le même numéro, ils seront gérés comme un seul champ.
Quand tu utilise ton gabarit et complète le premier champ, le texte
que tu y écris sera répété dans le second. Par contre, en sautant d'un
champ à l'autre avec le raccourcis Tab
, tu n'arrivera jamais sur le
second champ.
Si tu as 2 champs avec un numéro différent, il est posisble reprendre la
valeur du premier en tant que valeur par défaut du second. Dans ce
cas, en sautant d'un champ à l'autre avec le raccourcis Tab
, tu
arrivera sur le second champ que tu peux éditer si tu veux.
Pour ça, utilise la syntaxe suivante:
- Pour le champ
N
:$N
- Pour le champ
M
:${M:$N}
Il faut bien-sur remplacer N
et M
par les numéros que tu veux
utiliser. Et tu peux aussi définir une valeur par défaut pour le champ
N
.
Utiliser du code Elisp
pour modifier/définir une valeur
Quand tu définit le texte d'un gabarit, tu peux indiquer à Yasnippet
d'y insérer le résultat d'une instruction Elisp
. Au moment où le texte
du gabarit sera inséré, l'instruction Elisp
sera exécutée et son résultat
inséré au bon endroit.
Pour ça, tu peux écrire du code Elisp
directement dans le texte du
gabarit, à l'endroit souhaité, et l'entourer avec des
contre-guillemets. Voici un exemple qui insère son nom et son adresse
e-mail:
`(format "%s <%s>" user-full-name user-mail-address)`
Ici, le code Elisp
appelle à la fonction (format)
, avec 3
paramètres:
- La chaine de formatage:
"%s <%s>"
- Le contenu de la variable
user-full-name
, qui est le nom complet de l'utilisateur/utilisatrice - Le contenu de la variable
user-mail-adress
, qui est l'adresse e-mail de l'utilisateur/utilisatrice
Au moment où le texte du gabarit sera inséré, le code Elisp
de notre
exemple sera remplacé par ceci:
Le code Elisp
peut être utilisé n'importe où dans le texte du
gabarit, y compris comme valeur par défaut d'un champ.
Exécuter du code Elisp
sur une valeur de champ
Si tu définit 2 champs avec le même numéro, tu peux exécuter du code
Elisp
sur la valeur du premier champ et utiliser le résultat comme
valeur du second.
Par exemple:
Nom: ${1:Tapez un nom} Nom minuscule: ${1:$(downcase yas-text)}
Ici, on a 2 champs numéro 1, chacun définit une valeur par défaut et
le second appelle du code Elisp
. Cette fois, la syntaxe est un peut
différente: $(func yas-text)
, où func
est le nom de la fonction
que tu veux appeler et yas-text
est une variable représentant la
valeur rentrée pour le premier champ.
Avec cet exemple, au moment où le text du gabarit est inséré, on est
invité à écrire la valeur du premier champ. Ce qu'on écrit est recopié
comme valeur du second champ, mais en minuscule. Car la fonction
appelée est downcase
.
Où et comment sont sauvegardés les gabarits ?
Structure et format des gabarits sauvegardés
Chaque gabarit est sauvegardé sous la forme d'un fichier texte. Tous les gabarits sont regroupés dans un "dossier de gabarits".
Ce "dossier de gabarits" est structuré comme ceci:
- À la racine, un sous-dossier par mode
- Dans chaque sous-dossier, un fichier texte par gabarit
- Un dossier de second niveau peux être créé dans un dossier de mode, pour regrouper des gabarits
Définir les dossiers de gabarit
Il est possible d'avoir plusieurs "dossiers de gabarits". La liste des
dossiers où Yasnippet cherche des gabarits est stockée dans la variable
Emacs yas-snippet-dirs
.
Sa valeur par défaut est '("/home/your_home/.emacs.d/snippets")
,
mais tu peux la personnaliser en écrivant ceci dans le fichier
~/.emacs.d/init.el
:
(add-to-list 'yas-snippet-dirs "/path/to/new/directory")
Remplace "/path/to/new/directory"
par le chemin vers le dossier à
utiliser.
Cette variable peux être personnalisée par des paquets Emacs si ils fournissent des gabarits.
Grouper les gabarits
Pour regrouper plusieurs gabarits, il faut ajouter le champs group
dans l'en-tête du gabarit.
Comme ceci:
# -*- mode: snippet -*- # name: Dot dot dot # key: dots # group: specials # -- …
Au niveau du dossier des gabarits, tu es libre d'organiser les sous-dossiers de mode comme tu veux. Tu peux créer un dossier par groupe, ou tu peux tout garder au même niveau.
Le dossiers de second niveau ne créent pas automatiquement de groupes.
Utiliser des gabarits d'un mode à l'autre
Il est possible, pour un mode, d'utiliser les gabarits d'autres modes.
Pour ça, dans le dossier de gabarits, sous-dossier du mode, crée un
fichier texte nommé .yas-parent
. Dans ce fichier, écrit la liste des
autres modes dont tu souhaite utiliser les gabarits.
Par exemple: Pour le c-mode
, tu souhaite charger les gabarits des
modes fundamental-mode
et text-mode
.
Alors: Dans le dossier ~/.emacs.d/snippets/c-mode/
, crée un fichier
nommé .yas-parent
et écris y "fundamental-mode text-mode" (sans
les guillemets).
Maintenant, quand tu utilisera c-mode
, en plus de ses gabarits tu
aura accès à ceux de fundamental-mode
et text-mode
Astuces
Insérer un gabarit autour d'un texte
Si tu sélectionne du texte avant d'insérer un gabarit avec la fonction
Emacs yas-insert-snippet
, le texte sélectionné est supprimé puis le
texte du gabarit est inséré.
Mais il est possible de configurer Yasnippet afin de garder le texte sélectionné et d'insérer le celui du gabarit autour de cette sélection.
Pour ça, ajoute cette ligne dans le fichier ~/.emacs.d/init.el
:
(setq yas-wrap-around-region t)
Cette instruction Elisp
va associer la valeur t (True) à la variable
yas-wrap-around-region
. Cette variable indique à Yasnippet si il
doit, ou pas, garder le texte sélectionné et l'entourer avec celui du
gabarit inséré. Il faut évaluer cette instruction pour qu'elle soit
prise en compte.
Maintenant, si tu sélectionne du texte avant d'insérer un gabarit, le
texte sélectionné sera copié à la place du champ $0
du gabarit.
Par exemple:
Si on a ce gabarit pour du texte au format HTML:
# -*- mode: snippet -*- # name: Paragraphe # key: p # -- <p> $0 </p>
Qu'on sélectionne ce texte:
Ceci est un exemple de texte.
Et qu'on insère le gabarit p
, on obtient ceci:
<p> Ceci est un exemple de texte. </p>
Gabarit utilisable sous condition
Tu peux limiter l'utilisation d'un gabarit à une condition. Pour ça,
quand tu définit un gabarit, utilise le champ condition
et donne lui
comme valeur du code Elisp
. Le gabarit ne sera utilisable que si le
code Elisp
retourne une valeur autre que nil
.
Associer un raccourcis clavier à un gabarit
À la définition d'un gabarit, le champ binding
peut être utilisé
pour associer un raccourcis clavier à ce gabarit.
Par exemple, la valeur C-c r
correspondra à [Ctrl] + [C]
, puis
[R]
. Si tu utilise ce raccourcis, ton gabarit sera inséré.
Recharger la liste de gabarits
Si tu as ajouté un gabarit en copiant son fichier dans un dossier de gabarits, Yasnippet ne le verra pas tout seul.
Pour que Yasnippet scanne tous ses dossiers de gabarits, il faut
appeler la fonction Emacs yas-reload-all
(avec M-x
).
Ajouter des gabarits créés par la communauté
La communauté propose des gabarits, distribués sous la forme de
paquets Emacs. Il suffit d'en installer un et d'appeler la fonction
Emacs yas-reload-all
(avec M-x
) pour utiliser ses gabarits.
Voici une petite liste de paquets:
Nom du paquet | Description | Disponnible sur dépôt |
---|---|---|
yasnippet-classic-snippets |
Gabarits autrefois fournit avec Yasnippet | ELPA |
yasnippet-snippets |
Une grande collection de gabarits | NonGNU ELPA |
angular-snippets |
Gabarits pour Angular | MELPA |
awk-yasnippets |
Gabarits pour AWK | MELPA |
django-snippets |
Gabarits pour Django | MELPA |
haskell-snippets |
Gabarits pour Haskell | MELPA |
license-snippets |
Gabarits de licences logiciel | MELPA |
vala-snippets |
Gabarits pour Vala | MELPA |
Les dépôts ELPA
et NonGNU ELPA
sont déjà configuré dans Emacs.
MELPA
est un dépôt communautaire, qu'il faut ajouter.
Pour ajouter MELPA
, il suffit d'ajouter ceci dans ton fichier ~/.emacs.d/init.el
:
(require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
Après avoir évalué ces 2 lignes puis appelé la fonction Emacs
package-refresh-contents
, tu pourra installer des paquets en
provenance de MELPA
.
Pour information, les 2 lignes de code Elisp
ci-dessus vont: Charger
la fonctionnalité package
et ajouter une cellule dans la variable
package-archives
. La variable package-archives
est de type
associated list
. La cellule à pour clé le nom du dépôt, melpa
, et
pour valeur l'URL vers le dépôt.
Aller plus loin
Pour en apprendre plus sur l'utilisation de Yasnippet, je t'invite à aller lire ça documentation officielle.
Je t'invite également à voir l'épisode 06 de Emacs Rocks, dédié à Yasnippet. Je recommande même toutes les vidéos d'Emacs Rocks.
Conclusion
Yasnippet est un outils très pratique pour éviter de devoir ré-écrire plusieurs fois le même texte. Son utilisation est simple et on peut créer rapidement un gabarit à partir d'un morceau de texte déjà écrit.
Le système de champ, avec des valeurs par défaut, est particulièrement utile pour avoir un gabarit qu'on peut adapter en fonction de la situation.
Et enfin, l'utilisation de code Elisp
permet d'automatiser la
génération de certaines parties du gabarit. Parfois en fonction de la
valeur d'un ou plusieurs champs.
Un grand merci aux personnes qui ont travaillé sur ce super paquet Emacs.