Automatisation
Palier4 Cette page répond à une seule question : j’ai deux cents lignes à transformer de la même façon, et je refuse de le faire à la main. Il y a cinq outils pour ça, et le plus important est de savoir lequel sortir.
Le point répétable
. rejoue la dernière modification. C’est la touche la plus rentable de
l’éditeur, et pourtant elle déçoit souvent — parce que ce qu’elle rejoue dépend
entièrement de la façon dont on a formulé la commande.
Comparez deux manières de changer un mot :
Le résultat à l’écran est le même. Mais . après la première ne fera rien
d’utile, alors que . après la seconde rejoue « change le mot » n’importe où.
La règle qui en découle : une modification, un seul geste, exprimé avec un
opérateur et un objet. Le mode visuel est confortable pour un cas isolé, et il
casse la répétabilité.
L’idiome qui en tire le plus est la paire recherche + point :
C’est plus lent qu’un :%s sur deux cents occurrences, et plus rapide sur cinq —
avec l’avantage de voir chaque cas avant de le valider. Même logique avec les
déplacements de caractère : dt, puis ; puis . pour le suivant.
Le compteur ne se répète pas comme on croit
3dw supprime trois mots, et . en supprime trois de plus. Mais 2. ne rejoue
pas deux fois la commande : il la rejoue une fois en remplaçant son compteur
par 2. C’est utile pour corriger le tir — d2w puis 5. supprime cinq mots — et
déroutant si on ne l’attend pas.
Les registres
Un registre est un emplacement de stockage nommé. On préfixe la commande par
"{registre} :
| Registre | Contenu |
|---|---|
" | registre sans nom — dernière copie ou suppression |
0 | dernière copie explicite, jamais écrasée par une suppression |
1 à 9 | historique des suppressions de plus d’une ligne |
- | dernière suppression de moins d’une ligne |
a à z | registres nommés, à disposition |
_ | trou noir : y écrire ne conserve rien |
+ | presse-papiers du système |
% | nom du fichier courant |
. | dernier texte inséré |
: | dernière commande Ex |
/ | dernière recherche |
:registers affiche tout le contenu.
Trois usages qui reviennent constamment :
"0ppour coller après un remplacement visuel. Le collage en mode visuel écrase le registre sans nom avec le texte remplacé ;"0a gardé la copie."_dpour supprimer sans polluer. Utile juste avant un collage :"_dPremplace la sélection en conservant ce qu’on voulait coller.Ctrl + ren mode insertion ou en ligne de commande insère un registre.Ctrl + rpuis%insère le nom du fichier courant,Ctrl + rpuis/la dernière recherche — pratique pour construire un:%sà partir d’un motif qu’on vient de tester avec/.
Les macros
Une macro est une suite de frappes stockée dans un registre. C’est le même mécanisme, ce qui explique qu’on puisse la coller pour la relire.
q{a-z}: démarre l’enregistrementq: arrête@{a-z}: rejoue@@: rejoue la dernière macro utilisée{n}@a: rejoue n fois:'<,'>normal @a: applique la macro à chaque ligne d’une sélection
Trois principes rendent les macros fiables plutôt qu’aléatoires :
Ancrer le départ. Commencer par 0 ou ^ pour ne pas dépendre de la position
du curseur au moment du rejeu.
Se déplacer d’une ligne à la fin. Terminer par j permet d’écrire 100@a :
la macro avance elle-même. Sans ça, il faut repositionner le curseur entre chaque
rejeu.
Préférer un déplacement structurel à un compteur. f( est robuste si les
lignes varient, 3w ne l’est pas.
Une macro qui échoue s’arrête d’elle-même : si f( ne trouve pas de parenthèse,
le rejeu s’interrompt sur cette ligne plutôt que de faire n’importe quoi. C’est
une propriété à exploiter — lancer 1000@a sur un fichier de quatre-vingts lignes
fonctionne, la macro s’arrête au bout.
Corriger une macro sans la réenregistrer
"ap colle le contenu du registre a dans le buffer, sous forme de texte. On
corrige la ligne, puis "ay$ la recharge dans le registre. C’est nettement plus
agréable que de recommencer un enregistrement de quinze frappes pour une faute à
la douzième.
La commande globale
:g exécute une commande Ex sur toutes les lignes qui correspondent à un motif ;
:v sur toutes celles qui n’y correspondent pas. C’est l’outil le plus
sous-estimé de Vim.
Sa vraie puissance vient de la combinaison avec :normal, qui exécute des
touches du mode normal comme si on les tapait :
C’est le pont entre les deux mondes : :g sélectionne les lignes, :normal
applique la grammaire d’édition dessus. Une macro appliquée par :g cible
exactement les lignes concernées, là où un 100@a avancerait aveuglément.
Agir sur tous les fichiers d’un projet
La quickfix — remplie par :grep, :vimgrep ou les diagnostics LSP, voir
Recherche — se traverse aussi en écriture.
:cdo {cmd}: exécute la commande sur chaque entrée de la quickfix:cfdo {cmd}: une fois par fichier — préférable pour un:%s, qui traite déjà tout le fichier| update: enregistre, mais seulement si le fichier a changé
C’est le renommage à travers un dépôt entier, sans quitter l’éditeur et sans
plugin. Le contrôle est meilleur qu’un sed -i parce qu’on lit la liste avant
d’agir, et que u fonctionne — même si l’annulation reste par buffer.
Vérifier avant, pas après
:cdo sur trois cents entrées modifie trois cents endroits sans confirmation.
Ouvrir :copen et parcourir la liste prend dix secondes ; et un dépôt propre
avant l’opération vaut mieux que n’importe quelle relecture, puisque git diff
devient la vraie vérification.
Choisir l’outil
C’est la seule partie de cette page qui mérite d’être retenue par cœur.
| Situation | Outil |
|---|---|
| La même modification, quelques fois, en vérifiant chaque cas | ciw puis n. |
| Un remplacement textuel simple, dans un fichier | :%s/…/…/g |
| Le même remplacement, mais chaque cas mérite un œil | :%s/…/…/gc |
| Une transformation en plusieurs étapes, ligne par ligne | une macro |
| Une transformation qui ne concerne que certaines lignes | :g/motif/normal @a |
| Supprimer ou garder des lignes selon un motif | :g/…/d ou :v/…/d |
| Une modification à travers plusieurs fichiers | :grep puis :cfdo |
| Une transformation vraiment complexe, sur beaucoup de fichiers | un script, hors de l’éditeur |
La dernière ligne compte autant que les autres. Vim n’est pas obligé d’être la
réponse : ! filtre une sélection par une commande externe, et il y a des jours
où jq ou un script Python de dix lignes est la bonne réponse.