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-installet indiqueryasnippet - Ajouter ce morceau de code
Elispdans 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
Elispdans 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-modeyas-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:
booketread_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 surTabstoppera 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.