Saisies répétitives dans Vim
Imaginons que j'ai ce genre de fichier
dans lequel je veux remplacer .mp4
par .jpg
dans les balises img
uniquement :
<ul> <li><a href="11111111.jpg"><img src="11111111.jpg"></a></li> <li><a href="22222222.jpg"><img src="22222222.jpg"></a></li> <li><a href="33333333.mp4"><img src="33333333.mp4"></a></li> <li><a href="444444.jpg"><img src="444444.jpg"></a></li> <li><a href="555555.mp4"><img src="555555.mp4"></a></li> <li><a href="666666.mp4"><img src="666666.mp4"></a></li> <li><a href="77777777.jpg"><img src="77777777.jpg"></a></li> </ul>
Je veux obtenir :
<ul> <li><a href="11111111.jpg"><img src="11111111.jpg"></a></li> <li><a href="22222222.jpg"><img src="22222222.jpg"></a></li> <li><a href="33333333.mp4"><img src="33333333.jpg"></a></li> <li><a href="444444.jpg"><img src="444444.jpg"></a></li> <li><a href="555555.mp4"><img src="555555.jpg"></a></li> <li><a href="666666.mp4"><img src="666666.jpg"></a></li> <li><a href="77777777.jpg"><img src="77777777.jpg"></a></li> </ul>
Première possibilité, utiliser une commande rechercher-remplacer
C'est la possibilité qui me vient en premier à l'esprit : :%s/\(mp4.*\)mp4/\1jpg/ comme on peut faire avec sed
ou Perl
ou tout langage qui gère les expressions régulières. Mais il y a d'autres possibilités qui sont vraiment propres à Vim et que je n'utilise pas assez. J'écris cet aide-mémoire pour y penser plus souvent.
Deuxième possibilité, utiliser la commande .
qui sert justement à répéter la dernière modif, comme ceci :
- Je vais à la deuxième occurence de
mp4
, celle que je veux remplacer, par la commande /mp4 suivie de n (next) - Je fais ce pour changer jusqu'à la fin du mot (end of word), je saisis
jpg
puis Échap - Je fais n deux fois ou 2n pour aller à l'occurence suivante que je veux remplacer
- Je fais .
Troisième possibilité, utiliser une macro
Parce que si j'ai 100 lignes dans mon fichier, ça va devenir fastidieux.
- Je me mets sur la première occurence en cherchant /mp4
- Je lance l'enregistrement de la macro par qq (en fait ça enregistre la macro dans le registre q mais je pourrais faire qa pour enregister la macro dans le registre a)
- Je fais $ pour aller en fin de ligne
- Puis bbbbb (5 fois b) tout simplement pour revenir au début du mot
mp4
- Je fais ce, je saisis
jpg
puis Échap (même explication que plus haut) - Je fais q pour arrêter l'enregistrement
- Je fais :g/mp4/normal@q pour
- trouver globalement les lignes qui contiennent mp4
- leur appliquer la macro par @q
- comme si j'étais dans le mode normal
- Ou bien je sélectionne les lignes ou le paragraphe que je veux, et : puis j'ajoute normal@q (ici ça marche parce que toutes les images finissent en .jpg, mais s'il y avait des .JPG ou des .jpeg ça ne marcherait pas)
- Ou encore, pour le faire sur tout le fichier :%normal@q (ici il faudrait enlever la balise
ul
avant de faire ça)
Bref, c'est pas simple 😅 mais je trouve ça assez amusant et ça peut parfois être utile alors je le note ici.
Remarques :
- Pour afficher la macro q existante :echo @q et la macro est sauvée automatiquement dans l'historique des commandes donc on la retrouve d'une session sur l'autre
-
Si je me suis loupé en écrivant la macro q et je veux la modifier :
- Ouvrir un buffer vide :new
- Y coller le registre q "qp
- Modifier
- Coller la ligne de macro dans le registre q "qyy
- Fermer le buffer :q! (ici q c'est la commande quit, pas le registre q 😉)
Imaginons maintenant que j'ai le bloc de code suivant :
$this->set('foo', 'bidule'); $this->set('bar', 'machine'); $this->set('bazzz', 'truc'); $this->set('feeeeeh', 'chose');
Et imaginons que je veux ajouter true
en troisième paramètre de chaque appel de set
pour obtenir :
$this->set('foo', 'bidule', true); $this->set('bar', 'machine', true); $this->set('bazzz', 'truc', true); $this->set('feeeeeh', 'chose', true);
Je peux là encore utiliser une substitution avec une expression régulière mais je peux aussi utiliser une macro :
- Je me mets sur la première ligne, et je fais qq pour lancer l'enregistrement
- Je fais $ pour aller à la fin de la ligne
- Je fais ← ou h pour revenir à gauche d'un caractère (à vrai dire je n'utilise jamais les touches h, l, j et k pour me déplacer mais c'est bon à savoir qu'on peut les utiliser là)
- Je fais i pour insérer puis je saisis
, true
puis je fais Échap - Je termine l'enregistrement de la macro avec q
- Je sélectionne le reste du bloc où je veux ajouter le paramètre par ↓V} par exemple
- Je fais : et j'ajoute normal@q pour exécuter la macro q sur les lignes sélectionnées
Ça semble un peu "overkill" pour quelques lignes mais avec l'habitude, je ne suis pas sûr que ça soit moins rapide que faire la modif à la main sur chaque ligne.
Enfin, pour ajouter une ligne sous toutes celles qui matchent "ba"
Par exemple pour ajouter cette ligne : echo 'coucou';
En une seule commande : g/ba/normal! oecho 'coucou';
- g/ba/ signifie "matche globalement ba dans tout le fichier"
- normal! signifie "exécute cette commande sans récursivité (point d'exclamation)" pour éviter des mappings en boucle infinie
- o est la commande habituelle pour insérer sous la ligne courante ce qui permet d'avoir l'auto-indentation
Résultat :
$this->set('foo', 'bidule', true); $this->set('bar', 'machine', true); echo 'coucou'; $this->set('bazzz', 'truc', true); echo 'coucou'; $this->set('feeeeeh', 'chose', true);
Note : ça peut être lent s'il y a beaucoup d'occurences à traiter dans le fichier (par exemple plus de 100)