Licence CC BY-NC-SA

Travailler sur de nombreux fichiers avec (Neo)Vim — sur une seule vue

Usage des listes d’arguments et de tampons

Ce premier billet, d’une série de trois, fait suite à ma réponse à une interrogation de martoni sur LinuxFr.

L’objectif est de répondre, à travers des exemples pratiques, à la question de savoir comment on travaille avec plusieurs fichiers ; dans un premier temps dans une seule fenêtre…

Comme prérequis, il est demandé de :

  • avoir installé Vim ou Neovim ;
  • savoir utiliser une implémentation de vi pour un usage basique ;
  • savoir, optionnellement, utiliser un peu la ligne de commandes.
    (pas de panique, c’est surtout parce-que je n’ai pas testé en interface graphique.)

C’est parti ! :pirate:

la préparation

Avant d’entrer dans le vif du sujet, nous devons préparer le terrain.

Thé vert infusant dans un Zhong

alias

Afin d’éviter des commentaires de type « ça marche pas chez moi » ou « je n’obtiens pas la même chose » d’une part, et pour se focaliser sur le comportement par défaut d’autre part, on va lancer l’éditeur avec les options qui désactive toute personnalisation. Comme je privilégie une certain niveau de fainéantise et le fait de ne pas se répéter, on va se créer un alias au doux nom de dv pour « Default (Neo)Vim » tout simplement.

famille d’interpréteur de commandes pour Vim pour Neovim
Bourne shells (bash, ksh, zsh, etc.), Fish shell alias dv='vim --clean alias dv='nvim --clean
C shell (csh, tcsh, etc.) alias dv vim --clean alias dv nvim --clean
Microsoft PowerShell Function Default-Vim() { vim --clean } Set-Alias -Name dv -Value Default-Vim Function Default-Neo() { nvim --clean } Set-Alias -Name dv -Value Default-Neo
(PC/MS/Free/DR/etc.)-DOS ou (4/N)DOS, DR FlexOS, cmd.exe de Microsoft Windows (9x/ME/etc.), IBM OS/2, ReactOS doskey dv=vim --clean non disponible

Non vérifié, mais je pense que ça marche aussi si on lance l’implémentation GUI avec la même option --clean (sinon, pour Vim, il faut aussi ajouter l’option -U NONE.)

fichiers

On aura besoin de fichiers exemples. Créons un répertoire pour les accueillir (et pouvoir virer facilement l’ensemble quand on a fini.)

interpréteur de commandes commande de création de zt
shells Unix-like (sensibles à la casse) mkdir zt
Microsoft PowerShell New-Item -ItemType Directory -Path zt
*DOS, DR FlexOS, IBM OS/2, Microsoft Windows cmd.exe, ReactOS, etc. mkdir zt

On va se positionner dans ce répertoire (nommé ici zt comme « Zen Tests » par exemple, mais on peut choisir le nom de son choix) pour la suite.

interpréteur de commandes commande de position dans zt
shells Unix-like (sensibles à la casse) cd zt
Microsoft PowerShell Set-Location -Path zt
*DOS, DR FlexOS, IBM OS/2, Microsoft Windows cmd.exe, ReactOS, etc. chdir zt

Dans ce répertoire, créer un sous-répertoire d (comme « Directory » tout simplement) comme précédemment. Ensuite, on va créer les fichiers suivants par les moyens de son choix :1

Sixth green yielding herb all form gathering him without given living a
cattle saying dry one rule female divide image seed darkness said in a
unto morning can't under saying.
fichier zt/bleu.txt
Great you'll winged cattle seas she'd, forth male saying under saying
stars abundantly days. Can't fowl under gathering moved spirit. Own
shall tree itself there appear. Creepeth meat life fly.
fichier zt/cyan.txt
You're darkness creepeth. A upon heaven. Bring he, years of male.
Moving female said morning us beast fruit two evening divide light
tree give evening it rule morning herb be.
fichier zt/jaune.txt
Whose whales bring moved female beginning fifth. Behold firmament rule
itself. Life. Wherein. Was. It thing upon bearing it abundantly tree
that. Them brought likeness multiply fifth us set. Fly.
fichier zt/magenta.txt
Made seasons is evening which. Their herb creeping have divided spirit
second is unto to set fifth light of after fruit made meat of fruitful
is whales lesser every. Lights.
fichier zt/rouge.txt
He gathering all all image whose midst Beast they're moving. Face god sea.
Fruitful. So fruit which he fruitful they're Beast.  Can't the fish,
night set. Form hath life divided.
fichier zt/vert.txt
Said life also give from beast god two good every that fifth that
called own lesser fowl won't she'd. Beast unto creepeth. Third
morning called. Sea rule great he firmament.
fichier zt/d/nb.txt

conventions

Pour la suite, je parlerai souvent de « commande Ex ». Pas de panique, il s’agit des genres de commandes que l’on saisie en « ligne de saisie » après avoir tapé « : » et en terminant par «  ou ou  » ou alors «  ou ou  » …implicite. Dans ce contexte, on aura juste à saisir les commandes sans préfixer par les deux-points, c’est tout.

Je présenterai aussi, tout au long, des tableaux de synthèse des commandes en trois colonnes. La seconde, s’appellera « v.v » pour « Version de Vim » et comportera la version d’apparition de la commande sous cette forme.

Il y aura également des tableaux d’exercices en quatre colonnes dont la première, « exo », est la référence pour chaque ligne. On pourra utiliser ces références (un chiffre par sous-section, un point, un lettre minuscule) en commentaire pour pointer la ligne en question. La dernière et quatrième colonne indique un point de contrôle à réaliser obligatoirement.

Enfin, et surtout vers la fin, j’évoque des touches modificatrices et à un moment des touches directionnelles. Dans ce cas, j’utilise les symboles ISO/IEC 9995–7 & ISO 7000.2

On va maintenant pouvoir plonger au cœur de la préoccupation de ce journal. :)


  1. Concernant les fichiers…
    — Pub non sponsorisée : Ces textes, auxquels il ne faut pas chercher de signification, ont été créé en utilisant le site Dummy Text Generator avec comme paramètres : texte anglais, 30 mots, 1 paragraphe.
    — Note d’humour à l’adresse des plus geeks : Il est bien sûr possible d’utiliser cat (sous Unix-like) ou set-content (sous les dernières versions de Windows) pour créer les fichiers, ou juste avec l’éditeur que l’on va utiliser (cela aurait pu faire partir de l’exercice remarque…)
  2. Plus précisément, ISO-7000 “Graphical symbols for use on equipment”. Dans tous les cas, il faut avoir le support Unicode pour lire ce document.

les arguments

Pour commencer, lançons l’éditeur, comme convenu, avec les couleurs primaires (en synthèse multiplicative) et leur fusion.

$ dv cyan.txt jaune.txt magenta.txt d/nb.txt

Les quatre fichiers sont chargés par l’éditeur, le premier est affiché, mais où sont les autres ?

Media keys from the Logitech G15 gamer's keyboard

Contrairement aux apparences, il est prévu de pouvoir travailler avec plusieurs fichiers. Étrangement, c’est une fonctionnalité native de toute implémentation vi conforme POSIX, mais que peu de gens connaissent. Les commandes Ex prévues par le standard, pour naviguer entre les fichiers passés en argument, ne manqueront pas de faire penser aux lecteurs médias… dont l’interface perpétue l’héritage des enregistreurs à ruban

Voici la courte liste de ces commandes :

commandes POSIX ? icône ISO 7000 / IEC 60417 équivalent : pour…
:next, :n oui ⏭ : aller au fichier suivant
:previous, :prev non ⏮ : aller au fichier précédent
:rewind, :rew oui 🔃 : revenir au premier fichier
:file, :f oui ℹ : indiquer le fichier en cours, et sa position…2
:quit, :q oui ⏏ : tout fermer et quitter

Excepté :file, ces commandes sont affectées par les options autowrite et writeany de base…1 Tout comme :write, elles peuvent être suffixées de !. Maintenant, sans entrer dans ces détails, voyons les à l’œuvre :

exo commande de l’exercice message d’erreur résultant résultat de :f ensuite
1.a :quit E173: 3 more files to edit "cyan.txt" 3 lines --33%-- (1 of 4)
1.b :next "jaune.txt" 3 lines --33%-- (2 of 4)
1.c :quit E173: 2 more files to edit "jaune.txt" 3 lines --33%-- (2 of 4)
1.d :next "magenta.txt" 3 lines --33%-- (3 of 4)
1.e :q E173: 2 more files to edit "magenta.txt" 3 lines --33%-- (3 of 4)
1.f :n "d/nb.txt" 3 lines --33%-- (4 of 4)
1.g :n E165: Cannot go beyond last file "d/nb.txt" 3 lines --33%-- (4 of 4)
1.h :previous "magenta.txt" 3 lines --33%-- (3 of 4)
1.i :previous "jaune.txt" 3 lines --33%-- (2 of 4)
1.j :prev "cyan.txt" 3 lines --33%-- (1 of 4)
1.k :prev E164: Cannot go before first file "cyan.txt" 3 lines --33%-- (1 of 4)
1.l :n "jaune.txt" 3 lines --33%-- (2 of 4)
1.m :n "magenta.txt" 3 lines --33%-- (3 of 4)
1.n :rewind "cyan.txt" 3 lines --33%-- (1 of 4)
1.o :prev E164: Cannot go before first file "cyan.txt" 3 lines --33%-- (1 of 4)
1.p :n "jaune.txt" 3 lines --33%-- (2 of 4)
1.q :prev "cyan.txt" 3 lines --33%-- (1 of 4)
1.r :n "jaune.txt" 3 lines --33%-- (2 of 4)
1.s :rew "cyan.txt" 3 lines --33%-- (1 of 4)
1.t :prev E164: Cannot go before first file "cyan.txt" 3 lines --33%-- (1 of 4)
1.u :rew "cyan.txt" 3 lines --33%-- (1 of 4)
1.v :rew "cyan.txt" 3 lines --33%-- (1 of 4)
1.w :s/'ll/'d/ "cyan.txt" 3 lines --33%-- (1 of 4)
1.x :n E37: No write since last change (add ! to override) "cyan.txt" 3 lines --33%-- (1 of 4)
1.y :n! "jaune.txt" 3 lines --33%-- (2 of 4)
1.z :n "magenta.txt" 3 lines --33%-- (3 of 4)

Jusqu’ici, heureusement, rien de nouveau quand on vient d’une autre implémentation.

Vim a rajouté d’autres commandes pour compléter le tableau.

commandes v.v pour…
:args, :ar 1.? afficher la liste des arguments en encadrant le fichier courant
:last, :la 2.5 aller au dernier fichier
:first, :fir 6.0 revenir au premier fichier (synonyme de :rewind mais symétrique à :last)
:Next, :N 1.? aller au fichier précédent (synonyme de :previous mais symétrique à :next dans le principe des paires commandes n et N ou / et ?)
:wnext, :wn 1.? faire successivement :write puis :next (dans le même esprit que :wq)
:wprevious, :wp, :wNext, :wN 2.5 faire successivement :write puis :prev (dans le même esprit que :wq)

Plutôt que de longs discours, voyons cela en pratique :

exo commande de l’exercice message d’erreur résultant résultat de :args3 ensuite
2.a :Next cyan.txt [jaune.txt] magenta.txt d/nb.txt
2.b :n cyan.txt jaune.txt [magenta.txt] d/nb.txt et Press ENTER or type command to continue
2.c :first [cyan.txt] jaune.txt magenta.txt d/nb.txt
2.d :N E164: Cannot go before first file [cyan.txt] jaune.txt magenta.txt d/nb.txt
2.e :fir [cyan.txt] jaune.txt magenta.txt d/nb.txt
2.f :last cyan.txt jaune.txt magenta.txt [d/nb.txt]
2.g :n E165: Cannot go beyond last file cyan.txt jaune.txt magenta.txt [d/nb.txt]
2.h :la cyan.txt jaune.txt magenta.txt [d/nb.txt]
2.i :N cyan.txt jaune.txt [magenta.txt] d/nb.txt et Press ENTER or type command to continue
2.j :s/bring/wrote/ cyan.txt jaune.txt [magenta.txt] d/nb.txt
2.k :n E37: No write since last change (add ! to override) cyan.txt jaune.txt [magenta.txt] d/nb.txt
2.l :wn cyan.txt jaune.txt magenta.txt [d/nb.txt]
2.m :fir [cyan.txt] jaune.txt magenta.txt d/nb.txt

C’est un peu laborieux de se poser sur n’importe quel fichier dès que la liste est un peu longue… Sur une liste de dix fichiers par exemple, pouvoir passer de la deuxième à la neuvième nécessitait de faire « :n⏎ » sept fois ! Ici, on peut faire « :la:N⏎ » ; ce qui déjà un gain appréciable. Mais sur une liste de vingt fichiers, passer facilement de la troisième à la douzième reste un défi…

(Neo)Vim et d’autres implémentations ont également la possibilité d’ajouter un décompte…, et c’est bien pratique pour répondre à la problématique.

commandes v.v pour basculer sur l’édition du fichier qui est…
:𝓝next, :𝓝n 1.? 𝓝 rang(s) plus loin que le fichier en cours (cela revient à faire « :n⏎ » 𝓝 fois)
:𝓝wnext, :𝓝wn 1.? 𝓝 rang(s) plus loin, dans la liste, après avoir enregistré le fichier en cours
:𝓝previous, :𝓝prev, :𝓝Next, :𝓝N 1.? 𝓝 rang(s) plus tôt que le fichier en cours (cela revient à faire « :N⏎ » 𝓝 fois)
:𝓝wprevious, :𝓝wprev, :𝓝wNext, :𝓝wN 2.5 𝓝 rang(s) plus tôt, dans la liste, après avoir enregistré le fichier en cours
:𝓝argument, :𝓝argu, :argument 𝓝, :argu 𝓝 2.5 exactement en position 𝓝 dans la liste (i.e. « :f⏎ » indiquera « 𝓝 of » ⊤otal)

Le décompte « 𝓝 » est optionnel, et quand c’est omis c’est la valeur par défaut qui est utilisée. Hormis :argument dont la valeur par défaut est la position du fichier courant, c’est « 1 » pour les autres, ce qui nous ramène aux cas déjà vu.

exo commande de l’exercice message d’erreur résultant résultat de :args3 ensuite
2.n :2n cyan.txt jaune.txt [magenta.txt] d/nb.txt et Press ENTER or type command to continue
2.o :2n E165: Cannot go beyond last file cyan.txt jaune.txt [magenta.txt] d/nb.txt et Press ENTER or type command to continue
2.p :la cyan.txt jaune.txt magenta.txt [d/nb.txt]
2.q :2N cyan.txt [jaune.txt] magenta.txt d/nb.txt
2.r :2N E164: Cannot go before first file cyan.txt [jaune.txt] magenta.txt d/nb.txt
2.s :fir [cyan.txt] jaune.txt magenta.txt d/nb.txt
2.t :3argu cyan.txt jaune.txt [magenta.txt] d/nb.txt
2.u :s/wrote/bring/ cyan.txt jaune.txt [magenta.txt] d/nb.txt
2.v :2N E37: No write since last change (add ! to override) cyan.txt jaune.txt [magenta.txt] d/nb.txt
2.w :2wN [cyan.txt] jaune.txt magenta.txt d/nb.txt
2.x :1n cyan.txt [jaune.txt] magenta.txt d/nb.txt
2.y :1N [cyan.txt] jaune.txt magenta.txt d/nb.txt
2.z :1N E164: Cannot go before first file [cyan.txt] jaune.txt magenta.txt d/nb.txt

C’est sympa et j’apprécie personnellement ces petites attentions. Mais ce n’est pas tout…

liste manipulable

Pour permettre de remplacer nos couleurs primaires par leurs complémentaires (synthèse multiplicative), :args a été étendu pour accepter une nouvelle liste à la place de la liste courante. Dans un premier temps, on charge nos nouveaux fichiers dans l’ordre alphabétique. Ensuite, on change d’avis et on préfère l’ordre RVB et non BRV : la punition est toujours :args avec la liste dans le bon ordre. Idem si on décide de revenir à nos couleurs primaires.

exo commande de l’exercice message d’erreur résultant résultat de :args3 ensuite
3.a :args bleu.txt rouge.txt vert.txt [bleu.txt] rouge.txt vert.txt
3.b :args rouge.txt vert.txt bleu.txt [rouge.txt] vert.txt bleu.txt
3.c :ar cyan.txt jaune.txt magenta.txt [cyan.txt] jaune.txt magenta.txt

Si pendant longtemps les vimistes s’en sont contenté-e-s, ça reste pénible quand on plusieurs fichiers et que l’on souhaite en ajouter ou en retirer quelques uns car il faut ressaisir toute la liste. Avec la sixième version majeur, de nouvelles commandes vont simplifier la vie des usagers de l’éditeur sans pareil.4

exo commande de l’exercice message d’erreur résultant résultat de :args3 ensuite
3.d :$argadd d/nb.txt [cyan.txt] jaune.txt magenta.txt d/nb.txt
3.e :argadd rouge.txt [cyan.txt] rouge.txt jaune.txt magenta.txt d/nb.txt et Press ENTER or type command to continue
3.f :0arga vert.txt bleu.txt vert.txt bleu.txt [cyan.txt] rouge.txt jaune.txt magenta.txt d/nb.txt et Press ENTER or type command to continue
3.g :$argdelete vert.txt bleu.txt [cyan.txt] rouge.txt jaune.txt magenta.txt et Press ENTER or type command to continue
3.h :argdel *e.txt vert.txt bleu.txt [cyan.txt] magenta.txt
3.i :argd 404.txt E480 : No match: 404.txt vert.txt bleu.txt [cyan.txt] magenta.txt
3.j :2arga cyan.txt bleu.txt vert.txt bleu.txt bleu.txt cyan.txt [cyan.txt] magenta.txt et Press ENTER or type command to continue
3.k :argdedupe vert.txt bleu.txt [cyan.txt] magenta.txt

Au chapitre des nouvelles fonctionnalités, il y a la redoutable :argdo qui est peu connu…
Pour illustrer son fonctionnement, supposer que nous cherchons toutes les lignes contenant l’appostrephe (droite/dactylographique ici) suivie d’une lettre. La démarche, fastidieuse, qui fonctionne avec n’importe quelle implémentation est :

:" 3.l commencer par vert.txt
:rewind
:" 3.m y faire la recherche
:global/'[a-z]/#
  1 He gathering all all image whose mids Beast they're moving. Face god sea.
  2 Fruitful. So fruit which he fruitful they're Beast. Can't the fish,
Press ENTER or type command to continue
:" 3.n passer au fichier bleu.txt
:next
:" 3.o et faire la recherche ici
:global/'[a-z]/#
  3 unto morning can't under saying.
Press ENTER or type command to continue
:" 3.p passer au fichier cyan.txt
:n
:" 3.q et faire la recherche ici
:g/'[a-z]/#
  1 Great you'll winged cattle seas she'd, forth male saying under saying
  2 stars abundantly days. Can't fowl under gathering moved spirit. Own
Press ENTER or type command to continue
:" 3.r passer au fichier magenta.txt
:n
:" 3.s y faire la recherche aussi
:g/'[a-z]/#
Pattern not found: '[a-z]

Je vous laisse imaginer si on a une dizaine comme ça… Et là, arrive notre Circé :

:" 3.t depuis cyan.txt
:argument 3
:" 3.u faire la recherche partout
:argdo g/'[a-z]/#
"vert.txt" 3 lines, 179 bytes
  1 He gathering all all image whose mids Beast they're moving. Face god sea.
  2 Fruitful. So fruit which he fruitful they're Beast. Can't the fish,
"bleu.txt" 3 lines, 176 bytes
  3 unto morning can't under saying.
"cyan.txt" 3 lines, 194 bytes
  1 Great you'll winged cattle seas she'd, forth male saying under saying
  2 stars abundantly days. Can't fowl under gathering moved spirit. Own
"magenta.txt" 3 lines, 196 bytes
Pattern not found: '[a-z]
Press ENTER or type command to continue

Avec les implémentations autres que (Neo)Vim et ce :argdo, il aurait fallu demander à l’interpréteur de commandes du système avec le bon utilitaire et en lui ressaisissant la liste des arguments…

type d’interpréteur exemple de commande
shells Unix-like grep -n "'[a-z]" vert.txt bleu.txt cyan.txt magenta.txt
Microsoft PowerShell select-string -pattern "'[a-z]" -path vert.txt,bleu.txt,cyan.txt,magenta.txt
quelques *DOS pour PC find /n "'" vert.txt bleu.txt cyan.txt magenta.txt (cette commande ne connait pas d’expression rationnelle…)
Microsoft Windows cmd.exe findstr /n /r "'[a-z]" vert.txt bleu.txt cyan.txt magenta.txt

Mais toutes les opérations ne sont pas toujours réalisables ainsi, sans devoir scripter.

Au fait, :argdo et :argdelete utilisent aussi bien une plage (i.e. deux entiers « début,fin » dans l’ordre) qu’une position (i.e. un seul entier). Par défaut, c’est respectivement toute la liste et le fichier courant.

:" 3.v rechercher seulement dans bleu et cyan
:3,2argdo g/'[a-z]/#
Backwards range given, OK to swap (y/n)?y
"bleu.txt" 3 lines, 176 bytes
  3 unto morning can't under saying.
"cyan.txt" 3 lines, 194 bytes
  1 Great you'll winged cattle seas she'd, forth male saying under saying
  2 stars abundantly days. Can't fowl under gathering moved spirit. Own
Press ENTER or type command to continue

Pour en tirer pleinement profit, il faut connaître et pratiquer les commandes Ex. J’en profite pour glisser une réclame : le dernier « Atelier Vi » vous permettra de maîtriser pleinement cet aspect de cette famille d’éditeurs.
Mais avant, voici la synthèse des commandes que nous venons d’explorer :

commandes v.v pour…
:args 𝓕, :ar 𝓕 2.5 faire du(s) fichier(s) 𝓕 la nouvelle liste d’arguments
:next 𝓕, :n 𝓕 1.? faire du(s) fichier(s) 𝓕 la nouvelle liste d’arguments
:𝓝argadd 𝓕, :𝓝arga 𝓕 6.0 ajouter le(s) fichier(s) 𝓕 dans la liste, après le rang 𝓝 (par défaut courant), sans vérification de doublons… mais reste sur le fichier en cours
:𝓝argedit 𝓕, :𝓝arge 𝓕 6.0 ajouter le(s) fichier(s) 𝓕 dans la liste, après le rang 𝓝 (par défaut courant), sans vérification de doublons… puis bascule sur ce nouveau (premier) fichier
:𝓟argdelete, :𝓟argd 6.0 retirer le(s) fichier(s) de rang/plage 𝓟 de la liste
:argdelete ℳ, :argd 6.0 retirer de la liste, les fichiers correspondant au motif shell
:argdedupe, :argded 6.0 filtre la liste pour en retirer les doublons
:𝓟argdo 𝓒 6.0 exécuter la commande Ex 𝓒 sur le(s) fichier(s) du/de rang/plage 𝓟 de la liste

Quand (Neo)Vim se lance, la liste des fichiers en argument est récupérée et mise à disposition. Mais les usagers sont libres de redéfinir cette liste, et cette particularité est présente dans toute implémentation respectant le standard : il faut juste utiliser :next comme mentionné…

Si un ou plusieurs fichiers sont indiqués :

  1. Définir la liste d’arguments suivant les noms de fichiers spécifiés.
  2. Définir la position actuelle dans la liste d’arguments comme étant la première entrée de la liste d’arguments.
  3. Définir le chemin d’accès actuel au premier nom de fichier spécifié.

Dans le cas contraire :

  1. Il s’agit d’une erreur s’il n’y a plus de noms de fichiers dans la liste d’arguments après le nom de fichier actuellement référencé.
  2. Fixer le nom de chemin actuel et la référence de la liste d’arguments actuelle au nom de fichier suivant le nom de fichier actuellement référencé dans la liste d’arguments.

Remplacer le contenu du tampon d’édition par le contenu du fichier nommé par le chemin d’accès actuel. Si, pour une raison quelconque, le contenu du fichier n’est pas accessible, le tampon d’édition sera vide.

Dit simplement, quand des chemins de fichiers sont indiqués, « :next 𝓕 » a le même fonctionnement que « :args 𝓕 »… Juste que le nom de commande peut prêter à confusion (en tout cas ça en piège beaucoup qui n’ont pas pris le temps de bien lire et comprendre cette partie.)
En général, ce que l’on cherche à faire est répondu par :argedit 𝓕 …qui fait un :argadd 𝓕 puis va basculer dessus.

exo commande de l’exercice message d’erreur résultant résultat de :args3 ensuite
3.w :argedit bleu.txt vert.txt bleu.txt cyan.txt [bleu.txt] magenta.txt et Press ENTER or type command to continue
3.x :argdedupe vert.txt [bleu.txt] cyan.txt magenta.txt
3.y :argedit rouge.txt vert.txt bleu.txt [rouge.txt] cyan.txt magenta.txt et Press ENTER or type command to continue

Sur ce, on peut maintenant tout fermer.

:" 3.z
:quit

  1. Pour rappel ou information, writeany ou wa indique de forcer l’écriture sans devoir ajouter ! ; tandis que autowrite indique d’enregistrer les modifications avant d’exécuter une des commandes de navigation évoquées dans ce billet et toute commande de bascule en dehors d’un fichier modifié. Parmi les autres options qui peuvent influencer, sous (Neo)Vim, il y a en vrac et non exhaustivement : hidden, autowriteall, swapfile, writebackup ou wb, backup ou bk, backupcopy ou bkc, backupdir ou bdir, backupext ou bex, backupskip ou bsk, updatecount ou uc, updatetime ou ut, swapfile.
  2. L’option shortmess ou shm contient une liste de caractères indiquant les messages à raccourcir. Comme il contient « f », alors (Neo)Vim affiche la position du fichier sous la forme « (𝓝 of ⊤) » —en anglais— ou « (𝓝 sur ⊤) » —en français— au lieu de « (file 𝓝 of ⊤) » ou « (fichier 𝓝 sur ⊤) » respectivement.
  3. Les noms sont alignés sur des colonnes multiples de huit au moins, comme la commande ls sans argument des Unix-like. Ici, cet affichage n’est pas respecté et les noms sont juste séparés par une espace.
  4. Je ne doute pas que les adeptes de l’église Emacs me démontreront le contraire ; je n’ai juste pas cherché —ou comme dirait Bram, « I’m more of a vi-type of person » 😉 Attention que ceci n’est ni un appel au troll ni une invitation à quelque guerre d’éditeurs de texte.

les tampons

Pour poursuivre, lançons l’éditeur, comme convenu, avec les fichiers dont le nom (sans extension) se termine par une voyelle.

$ # sur les Unix-like et autres on peut juste faire
$ # dv *[aoeuiy].txt # mais y a que "eau" ici
$ dv bleu.txt jaune.txt magenta.txt rouge.txt

Les quatre fichiers sont chargés par l’éditeur, le premier est affiché et les autres sont en quelque sorte derrière…

tampon encreur

Dans la session précédente, on avait vu que la liste de fichiers passés en argument est copié dans une liste d’arguments que nous pouvons manipuler à loisir. À partir de la version 2.4, Vim copie également la liste de fichiers passés en argument dans une nouvelle liste appelée liste des tampons… Cette nouvelle/seconde liste a ses commandes qui commencent par « b » comme « buffer », c’est à dire tampon.

commandes v.v icône : pour…
:bprevious, :bp, :bNext, :bN 2.4 ⏮ : aller au tampon précédent
:bnext, :bn 2.4 ⏭ : aller au tampon suivant
:brewind, :br 2.4 ↩️ : aller au premier tampon
:bfirst, :bf 6.0 ↩️ : aller au premier tampon
:blast, :bl 2.5 ↪️ : aller au dernier tampon
:buffers, :ls, :files 2.4 ℹ️ : afficher la liste des tampons

Excepté pour le listing, les commandes de navigation sont affectées par les options autowrite et writeany entre autres… et peuvent être suffixées de ! Il ne faut pas se fier à la ressemble avec l’autre liste ; déjà celle-ci boucle… et ne marque pas les fichiers comme édités de la même façon… (à partir de 4.g, après avoir fait le tour, on peut quitter ; mais jusque là le message indique toujours qu’il y a trois autres fichiers en attente…)

exo commande de l’exercice message d’erreur résultant tampon actif (ligne avec %a de :ls)1
4.a :quit E173: 3 more files to edit 1 "bleu.txt"
4.b :bnext 2 "jaune.txt"
4.c :quit E173: 3 more files to edit 2 "jaune.txt"
4.d :bnext 3 "magenta.txt"
4.e :q E173: 3 more files to edit 3 "magenta.txt"
4.f :bn 4 "rouge.txt"
4.g :bn 1 "bleu.txt"
4.h :blast 4 "rouge.txt"
4.i :bNext 3 "magenta.txt"
4.j :bfirst 1 "bleu.txt"
4.k :bprev 4 "rouge.txt"

Comme les tampons sont numérotés par ordre de création, et que cette numérotation ne changera pendant toute la session de travail, on peut s’aider de cette numérotation dans la navigation.

commandes v.v pour aller au tampon…
:𝓝bprevious, :𝓝bp, :bprevious 𝓝, :bp 𝓝, :b𝓝Next, :𝓝bN, :bNext 𝓝, :bN 𝓝 2.4 situé 𝓝 rang plus tôt dans toute la liste
:𝓝bnext, :𝓝bn, :bnext 𝓝, :bn 𝓝 2.4 situé 𝓝 rang plus loin dans toute la liste
:𝓝buffer, :𝓝b, :buffer 𝓝, :b 𝓝 2.5 exactement en position 𝓝 dans toute la liste
:edit #𝓝, :e #𝓝, 𝓝⎈^ ?.? exactement en position 𝓝 dans toute la liste
:buffer 𝓕, :b 𝓕 2.7 de nom 𝓕 (peut être partiel mais doit être distinct) dans la liste
:𝓝bmodified, :𝓝bm, :bmodified 𝓝, :bm 𝓝 2.4 en position 𝓝 dans la liste des tampons modifiés
:edit #, :e #, ⎈^ ?.? alternatif (i.e. où on était précédemment)

Certaines commandes nous sont déjà familières (cf. section précédente) ; les deux dernières seront élucidées plus loin. Ici aussi, l’indication du rang est optionnel et vaut celui du tampon actuel pour :buffer et l’unité pour les autres.

exo commande de l’exercice remarque tampon actif (vérifié par :ls)1
4.l :3bp de la position 4 (dernière position en 4.k), on recule de 3 1 "bleu.txt"
4.m :2bp de la position 1, on recule de 2 (sachant que la liste boucle) 3 "magenta.txt"
4.n :5bn de la position 3, on avance de 5 (sachant que la liste boucle) 4 "rouge.txt"
4.o :bn 2 de la position 4, on avance de 2 (toujours en bouclant) 2 "jaune.txt"
4.p :bp 3 de la position 2, on recule de 3 (toujours en bouclant) 3 "magenta.txt"
4.q :1b on se met exactement en première position (donc :bfirst) 1 "bleu.txt"
4.r :b 2 on se met pile en deuxième position (soit :bn ici) 2 "jaune.txt"
4.s :$b on se met exactement en dernière position (donc :blast) 4 "rouge.txt"
4.t :b 3 on se met exactement en troisième position (soit :bp ou :bN ici) 3 "magenta.txt"
4.u :bN 1 de la position 3, on recule de 1 (soit juste :bp ou :bN sans argument) 2 "jaune.txt"
4.v :bn 1 de la position 2, on avance de 1 (soit juste :bn sans argument) 3 "magenta.txt"
4.w :buffer ici, sans numéro, on reste à la même place 3 "magenta.txt"

Voilà pour l’introduction rapide.

tampons variés

Il va falloir maintenant décortiquer un peu plus ce concept (de tampon) et l’utilisation de cette liste (de tampons).

définitions et précisions

Le tampon (ou buffer en anglais) représente le contenu en mémoire (comprendre en cours de manipulation et donc pas l’état réel du fichier tant que la mémoire n’est pas écrite dans le fichier.) C’est le mode de fonctionnement de tout éditeur de texte (et pratiquement tous les programmes) : on n’écrit et lit pas le fichier sur le disque à chaque action, ce serait peu performant et augmenterait les risques de corruption du fichier.

Il arrive que le fichier ne puisse pas tenir en mémoire (peu de gens connaissent cette situation aujourd’hui n’est-ce pas ?) La stratégie hérité du Vi historique est d’utiliser un fichier d’échange (ou swap file en anglais) pour garder le travail en cours (le fichier effectif n’est écrit que si l’on demande d’une façon —explicite— ou d’une autre —implicite par des options de configuration— de le faire) et de ne charger que la portion en cours d’édition. Cela peut se ressentir.

Il n’y a que les éditeurs codés naïvement qui chargent tous les fichiers en mémoire. (Neo)Vim et d’autres vont, pour ne pas saturer la mémoire, travailler en pratique avec des listes chainée (ou linked lists en anglais). Il y en a plusieurs au cours de la session, et chacune a son jeu de commandes et son compteur…

des listes différentes

Chaque liste a ses références internes et son compteur, ce qui peut être source de confusion quand on ne comprend pas ce qu’on fait ou quand on manque de vigilance.

exo commande de l’exercice message d’information résultant tampon actif (ligne avec %a de :ls)1
5.a :args [bleu.txt] jaune.txt magenta.txt rouge.txt 3 "magenta.txt"
5.b :file "rouge.txt" 3 lines --33%-- ((1) of 4) 3 "magenta.txt"

Bien que les deux listes semblent avoir les mêmes éléments ici, on se trouve sur le troisième élément de la liste des tampons, mais sur le premier élément de la liste d’arguments : les commandes qui manipulent chacune de ces listes ne sont pas interchangeables !
Pour nous prévenir, :file dans (Neo)Vim met l’argument courant entre parenthèses pour dire que ce n’est pas celui qui est affiché/actif.

exo commande de l’exercice message d’information résultant tampon actif (ligne avec %a de :ls)1
5.c :argd *e.txt 3 "magenta.txt"
5.d :args [bleu.txt] magenta.txt 3 "magenta.txt"
5.e :file "rouge.txt" 3 lines --33%-- ((1) of 2) 3 "magenta.txt"

Les deux listes n’ont plus les mêmes éléments… Et là, on a un indice de l’utilité d’avoir deux listes :

  • Celui des tampons est, comme son nom l’indique, réservé aux documents “ouverts” dans l’éditeur ; tous.
  • Celui des arguments est par contre une “sélection”, parmi ces documents, sur laquelle on travaille (ou on va agir avec :argdo par exemple.)

On peut ainsi avoir une sélection de fichiers que l’on édite activement, tout ayant une série de fichiers ouverts pour référence (utile notamment en développement logiciel où c’est un peu ce que font des environnement de développement en référençant le répertoire du projet.)

dans divers états

Revenons à nos tampons. Ils sont listés par les commandes synonymes :buffers, :ls et :files (avec “s” final) …qui affichent quatre colonnes dans l’ordre :

  1. Le numéro du tampon, aligné à droite sur la troisième colonne.
  2. Les différents états sur cinq colonnes (de la quatrième à la huitième incluse.)
  3. Le nom du fichier, aligné à gauche, entre guillemets doubles droits (ce qui permet de distinguer ses espaces du reste.)
  4. La ligne du curseur dans le fichier à partir de la quarantième colonne chez moi. line 0 (ou peut-être ligne 0 chez vous) indique alors un tampon pas encore visité.

Pour les différents états justement, nous en avons déjà croisé deux :

  • actif quand le tampon (ou, pour simplifier, le fichier) est affiché à l’écran.
  • caché (ou juste masqué) quand, à l’inverse, le tampon n’est pas visible.

Cet état d’affichage est indiqué par un caractère en sixième colonne (en tout, soit la troisième colonne indiquant les états)

  • « a » pour indiquer que le fichier est chargé en mémoire et son tampon affiché (soit active en anglais.) Cette indication est apparue avec la version 6.0 de Vim.
  • « h » pour indiquer que le fichier est chargé en mémoire mais que son tampon est caché (soit hidden en anglais.)
  • «   » (espace) quand le fichier n’est pas chargé en mémoire, mais bien référencé dans la liste.

La colonne suivante (la quatrième ou septième), ajoutée à partir de la version 6.0 de Vim, donne le statut système du fichier. Actuellement, on a :

  • pour un fichier normal
    • « - » indique que (Neo)Vim n’a pas le droit de le modifier —typiquement quand on lance l’éditeur en mode lecture seule par exemple
    • « = » indique que le (Neo)Vim ne peut pas le modifier —typiquement quand le fichier est en lecture seule au niveau système
    • «   » (espace) indique le cas normal, donc rien à signaler.
  • pour un terminal (fonctionnalité apparue dans Vim 8.0)
    • « ? » sans tâche (ou job en anglais) associée
    • « R » avec un tâche en cours (soit running en anglais)
    • « F » avec une tâche terminée (soit finished en anglais)

La dernière colonne, ajoutée à partir de la version 6.0 de Vim, va plus nous intéresser

  • « + » indique que le tampon a été changé (aussi bien une modification d’édition qu’un changement d’encodage par exemple) mais pas encore enregistré dans le fichier…
  • « x » indique que le tampon est en erreur de lecture (soit une corruption mémoire, soit une corruption de fichier)
exo commande de l’exercice message d’erreur résultant états du tampon 3 (dans :ls)
5.f :s/bring/carry %a +
5.g :bn E37: No write since last change (add ! to override) %a +
5.h :w %a
5.i :s/carry/bring %a +
5.j :bn! #h +
5.k :3b %a +
5.l :bf E37: No write since last change (add ! to override) %a +
5.n :bf! #h +
5.o :wa #h
5.p :bm E84: No modified buffer found #h
5.q :3b %a
5.r :s/bring/bring #a +
5.s :bl! #h +
5.t :bm %a +

C’est ici que :bmodified prend son sens. ;) Cette commande cycle dans une sous-liste (temporairement et automatiquement générée) de tampons modifiés.

Il est possible d’afficher diverses sous-listes de tampons en passant les caractères de statuts à filtrer en argument. À cela s’ajoute le caractère-filtre t pour afficher l’heure de modification au lieu de la ligne du curseur, et trier par horodatage décroissant au lieu de numéro d’ordre de création du tampon.

:" 5.u tous par défaut, par numéros croissant
:buffers
  1      "bleu.txt"                     line 1
  2      "jaune.txt"                    line 1
  3 %a + "magenta.txt"                  line 1
  4 #    "rouge.txt"                    line 1
Press ENTER or type command to continue
:" 5.v tous les modifiés
:ls +
  3 %a + "magenta.txt"                  line 1
Press ENTER or type command to continue
:" 5.w tous les modifiés et les actifs
:ls +a
  3 %a + "magenta.txt"                  line 1
Press ENTER or type command to continue
:" 5.x lister par horodatage décroissant
:ls t
  3 %a + "magenta.txt"                  20:11:43
  4 #    "rouge.txt"                    19:57:20
  1      "bleu.txt"                     19:50:47
  2      "jaune.txt"                    16:52:00
Press ENTER or type command to continue

Pour finir, il faut mentionner trois derniers caractères

  • « % », en deuxième position, représente le tampon courant…
  • « # », en deuxième position, représente le tampon alternatif…
  • « u », en première position, représente un tampon temporaire non listé par défaut (soit unlisted en anglais), sauf quand on suffixe la commande par !. Cela se produit par exemple quand on ouvre la fenêtre d’aide ou quand on donne un nom alternatif à un tampon.

courant et alternatif

Ç’est une fonctionnalité historique de Vi et qui est requise par la spécification

Deux noms de chemin, nommés courant et alternatif, sont maintenus par l’éditeur. Toutes les commandes Ex qui prennent des noms de fichiers comme arguments doivent les définir comme suit :

  1. Si un argument de fichier est spécifié aux commandes Ex :edit, :ex, ou :recover, ou si une commande Ex :tag remplace le contenu du tampon d’édition.
    1. Si la commande remplace le contenu du tampon d’édition, le nom de chemin courant doit être défini sur l’argument de fichier ou le fichier indiqué par la balise, et le nom de chemin alternatif doit être défini sur la valeur précédente du nom de chemin courant.
    2. Dans le cas contraire, le nom d’accès alternatif correspond à l’argument du fichier.
  2. Si un argument de fichier est spécifié pour la commande Ex :next :
    1. Si la commande remplace le contenu du tampon d’édition, le chemin d’accès actuel est défini sur le premier argument de fichier, et le chemin d’accès alternatif est défini sur la valeur précédente du chemin d’accès actuel.
  3. Si un argument de fichier est spécifié à la commande Ex :file, le nom d’accès courant est défini à l’argument de file, et le nom d’accès alternatif est défini à la valeur précédente du nom d’accès courant.
  4. Si un argument de fichier est spécifié aux commandes Ex :read et :write (c’est-à-dire lors de la lecture ou de l’écriture d’un fichier, et non par l’option de commande nommé :edit par l’appel de shell), ou si un argument de fichier est spécifié dans la commande Ex :xit, le nom du chemin d’accès actuel doit être défini comme l’argument de fichier :
    1. Si le chemin d’accès actuel n’a pas de valeur, il est remplacé par l’argument fichier.
    2. Dans le cas contraire, le nom d’accès alternatif est défini comme étant l’argument du fichier.

Si le nom d’accès alternatif est défini à la valeur précédente du nom d’accès actuel alors que le nom d’accès actuel n’avait pas de valeur précédente, le nom d’accès alternatif n’a pas de valeur en conséquence.

Pour la petite histoire, avant la version 2.5 de Vim, :files servait à afficher le fichier alternatif (et le fichier courant ?) Cela a été fusionné dans les listing en utilisant les caractères prévus par la spécifications par ailleurs.

Non-<backslash>-escaped % characters appearing in file arguments to any ex command shall be replaced by the current pathname; unescaped # characters shall be replaced by the alternate pathname. It shall be an error if % or # characters appear unescaped in an argument and their corresponding values are not set.

L’intégration est poussée au point de nous permettre de basculer sur le tampon alternatif sans connaitre son numéro, sous réserve que celui-ci soit nommé…

exo commande de l’exercice message d’erreur résultant tampon actif (ligne avec %a de :ls)1
5.y :e# 4 "rouge.txt"
5.z :e# 3 "magenta.txt"

Bon à savoir : on peut utiliser # avec toutes les commandes de tampon attendant une position 𝓝 …ou parfois un chemin 𝓕 !

modifiable aussi

Un certain nombre de commandes historiques, d’ouverture et d’écriture de fichiers, avec agissent sur cette liste.

exo commande de l’exercice remarque ou message résultant tampon actif (ligne avec %a de :ls)1 et état
:b3 position après 5.z 3 %a + "magenta.txt"
6.a :w enregistrement du tampon dans le fichier 3 %a "magenta.txt"
6.b :f fuchsia.txt nouveau tampon qui remplace l’autre (mais ls! nous montre un cinquième…) 3 %a "fuchsia.txt"
6.c :args [bleu.txt] fuchsia.txt 3 %a "fuchsia.txt"
6.d :s/fifth/sixth ce tampon est modifié et toujours pas enregistré (on peut même vérifier, depuis une autre session parallèle par exemple, que ce fichier n’existe pas encore dans notre répertoire de travail) 3 %a + "fuchsia.txt"
6.e :w pourpre.txt on va enregistrer une copie sous un nom alternatif (encore ls! pour exhiber le sixième…) 3 %a + "fuchsia.txt"
6.f :undo 1 change; before #1 23:30:02 (pareil que u ; par contre, noter que le tampon est dans son état initial mais toujours pas enregistré sur le disque…) 3 %a "fuchsia.txt"
6.g :bn comme ce n’est pas marqué comme modifié, on peut le quitter sans lever d’alerte 4 %a "rouge.txt"
6.h :bp on revient sur un tampon vide… (normal puisqu’on ne l’avait pas enregistré) 3 %a "fuchsia.txt"
6.i :r magenta.txt complétons le (sur les lignes suivantes) avec le contenu de magenta 3 %a + "fuchsia.txt"
6.j :w enregistrons cette fois sur le disque 3 %a "fuchsia.txt"
6.k :e pourpre.txt basculons sur pourpre, qui existe et n’est pas chargé (mais était dans la liste et on voit qu’il conserve son rang…) 6 %a + "pourpre.txt"
6.l :e violet.txt basculons sur violet, qui n’a jamais été dans la liste (et de toute façon n’existe pas) : on constate qu’on créé un nouveau tampon sur lequel on bascule 7 %a "violet.txt"
6.m :view pourpre.txt rebasculons sur pourpre en mode lecture 6 %a= "pourpre.txt"
6.n :s/sixth/fifth W10: Warning: Changing a readonly file 6 %a=+ "pourpre.txt"
6.o :w E45: 'readonly' option is set (add ! to override) 6 %a=+ "pourpre.txt"
6.p :e E37: No write since last change (add ! to override) 6 %a=+ "pourpre.txt"
6.q :e! rechargeons le tampon dans son état initial connu 6 %a "pourpre.txt"
6.r :e#7 rebasculons sur violet dont on connait le numéro (synonyme donc de :b7 qui aurait été plus rapide) 7 %a "violet.txt"

Ce petit tour dans la gamme violacée nous a permis de voir un peu les subtilités de :edit et :write, ainsi que d’approfondir la notion de fichier alternatif avec :file et :write. Au passage, nous avons observé divers états de tampon décrits plus tôt.

D’autres commandes Ex ont été ajoutées par la suite pour faciliter encore plus les manipulations de cette liste.

commandes v.v pour…
:edit 𝓕, :e 𝓕, :visual 𝓕, :vi 𝓕 1.? ajouter le fichier 𝓕 à la liste des tampons s’il n’y est pas encore, puis le charge et bascule dessus (comme :buffer 𝓕)
:edit, :e, :visual, :vi 1.? recharger le tampon courant
:view 𝓕, :vie 𝓕 1.? ajouter le fichier 𝓕 à la liste des tampons s’il n’y est pas encore, puis le charge en lecture seule et bascule dessus
:file 𝓕, :f 𝓕 1.? donner un nom à un tampon anonyme (ce qui permet de faire plus tard :write sans spécifier de nom), ou donne un nouveau nom 𝓕 au tampon courant (l’ancien devient donc nom listé)
:badd 𝓕, :bad 𝓕 5.2 ajouter le fichier 𝓕 à la liste des tampons s’il n’y est pas encore, sans le charger (on reste sur le tampon courant)
:balt 𝓕 9.0 faire comme « :badd 𝓕 » et mettre aussi 𝓕 comme nom alternatif
:enew, :ene 6.0 créer un nouveau tampon anonyme (associé à aucun fichier) et basculer dessus
:0file, :0f 7.0 retirer le nom du tampon courant
:𝓟write, :𝓟w 1.? enregistrer le tampon courant (nommé) dans son fichier
:𝓟write 𝓕, :𝓟w 𝓕 1.? enregistrer le tampon anonyme (associé à aucun fichier) courant dans le chemin 𝓕 et en faire son nom dans la liste, ou donner un nouveau nom 𝓕 au tampon courant (l’ancien devient donc non listé) et l’enregistrer dans ce chemin
:saveas 𝓕, :sav 𝓕 6.0 faire « :write 𝓕 » puis « :edit # » sans devoir recharger le fichier
:𝓟update, :𝓟up 5.0 faire « :𝓟write » si le tampon est dans un état modifié
:𝓟update 𝓕, :𝓟up 𝓕 5.0 faire « :𝓟write 𝓕 » si le tampon a été modifié
:wall, :wa 6.? enregistrer tous les tampons modifiés
:𝓟bdelete, :𝓟bdel, :𝓟bd, :bdelete 𝓟, :bdel 𝓟, :bd 𝓟 2.4 supprimer le(s) fichier(s) en position 𝓟 de la liste des tampons et le(s) décharger de la mémoire
:bdelete 𝓕, :bdel 𝓕, :bd 𝓕 2.5 supprimer le fichier 𝓕 de la liste des tampons et le décharger de la mémoire
:bdelete 𝓛, :bdel 𝓛, :bd 𝓛 2.7 supprimer les fichiers 𝓛 (noms et rangs séparés par la virgule) de la liste des tampons et le décharger de la mémoire
:𝓟bwipeout, :𝓟bwipe, :𝓟bw, :bwipeout 𝓟, :bwipe 𝓟, :bw 𝓟 6.? supprimer le(s) fichier(s) en position 𝓟 de la liste des tampons, le(s) décharger de la mémoire et supprime tout élément lié en mémoire
:bwipeout 𝓕, :bwipe 𝓕, :bw 𝓕 6.? supprimer le fichier 𝓕 de la liste des tampons, le décharger de la mémoire et supprime tout élément lié en mémoire
:𝓟bunload, :𝓟bun, :bunload 𝓟, :bun 𝓟 2.5 garder le(s) fichier(s) en position 𝓟 dans la liste des tampons mais le(s) décharger de la mémoire
:bunload 𝓕, :bun 𝓕 2.5 garder le fichier 𝓕 dans la liste des tampons mais le décharger de la mémoire
:bunload 𝓛, :bun 𝓛 2.7 garder les fichiers 𝓛 (noms et rangs séparés par la virgule) dans la liste des tampons mais le décharger de la mémoire
:𝓟bufdo 𝓒 6.0 exécuter la commande Ex 𝓒 à la plage 𝓟 (par défaut tous) de tampons

Nous n’allons pas les passer (toutes) en revue car, au stade où nous en sommes, elles parlent d’elles-mêmes pour la plupart.

exo commande de l’exercice remarque ou message résultant tampon actif (ligne avec %a de :ls)1 et état
6.s :enew création d’un tampon anonyme vide et bascule dessus 8 "[No Name]" (ou peut-être "[Sans Nom]" chez vous)
6.t :w d/ti.txt lui donner un nom et l’enregistrer 8 "d/ti.txt"
6.u :badd d/nb.txt mettre dans la liste ce fichier aussi (un ls permet de s’en rendre compte) 8 "d/ti.txt"
6.v :args [bleu.txt] fuchsia.txt 8 "d/ti.txt"
6.w :bdelete d/nb.txt on change d’avis et on le retire (un ls permet de s’en rendre compte) 8 "d/ti.txt"
6.x :argadd d/nb.txt en change encore d’avis et on le rajoute (un ls permet de voir qu’il a retrouvé sa place) 8 "d/ti.txt"
6.y :args [bleu.txt] d/nb.txt fuchsia.txt 8 "d/ti.txt"

Les deux listes ont beau être indépendantes, il y a des commandes qui influent sur les deux et c’est logique… :argadd ajoute bien un fichier à la liste d’arguments, mais comme les liste des tampons est le comptable de tous les tampons en cours alors il y est ajouté aussi s’il n’y était pas déjà. L’inverse n’est pas vrai : :badd ne rajoute rien dans la liste d’arguments. Mieux, les actions sur la liste des tampons n’impacte pas la liste des arguments, et :bdelete laissera le fichier dans la liste d’arguments s’il y est (et si on bascule sur cet argument, paf il se retrouve référencé dans la liste des tampons…) Sans surprise, :bufdo impactera tous les fichiers de la liste d’arguments (sauf, comme on vient de le dire, si retiré de la liste de tampons), tandis que :argdo ne vera qu’une portion de la liste de tampons.

Pour finir, Vim a aussi augmenter les commandes de sortie/arrêt…

commandes v.v pour…
:quit, :q 1.? quitter l’éditeur sans enregistrer
:wq 1.? quitter l’éditeur après avoir enregistré le tampon courant, soit successivement « :w puis :q »
:𝓟wq 𝓕 1.? quitter l’éditeur après avoir enregistré sous 𝓕, soit successivement « :𝓟w 𝓕 puis :q » avec une plage 𝓟 optionnelle (par défaut toutes les lignes)
:xit, :x, ZZ 1.? quitter l’éditeur après avoir enregistré le tampon courant s’il est modifié, soit successivement « :update puis :q »
:𝓟xit 𝓕, :𝓟x 𝓕 1.? quitter l’éditeur après avoir enregistré sous 𝓕 s’il y a eu modification, soit successivement « :𝓟up 𝓕 puis :q » avec une plage 𝓟 optionnelle (par défaut toutes les lignes)
:exit, :exi 2.7 synonyme de :xit
:𝓟exit 𝓕, :𝓟exi 𝓕 2.7 synonyme de :𝓟xit
:quitall, :quita, :qa 6.0
:wqall, :wqa, :xall, :xa 2.5 quitter l’éditeur après avoir enregistrer tous les tampons nommés modifiés, soit successivement « :wa puis :q »

Sur ce, il est l’heure de plier bagage.

:"wall enregistrer tous les fichiers (dont violet.txt vide)
:"quit puis fermer la boutique pour aujourd'hui
:" 6.z les deux actions peuvent se faire en une fois
:wqall

  1. Nous ne reproduisons pas toute la sortie de la commande :buffers ou :ls ni exactement la ligne qui nous intéresse (celle avec %a dans la seconde colonne) ; nous indiquons juste le numéro (première colonne) et le nom (troisième colonne)…

les positions

Pour la route, lançons l’éditeur avec des fichiers choisis :

$ dv fuchsia.txt vert.txt cyan.txt bleu.txt

Le premier des quatre tampons est affiché, nous connaissons la chanson.

marqueur de scène de crime

positions remarquables

Il y a une fonctionnalité historique peu connue, ou sous-estimée, et pourtant bien utile : on peut, comme des experts sur une scène d’infraction, poser des marqueurs à des points clés du tampon.

commande v.v pour…
` 1.? sauter à la ligne et la colonne du marqueur ℳ
' 1.? sauter au premier caractère non blanc de la ligne du marqueur ℳ ; indiquer, dans une commande Ex, la ligne du marqueur ℳ
m 1.? poser le marqueur ℳ à la position du curseur
:𝓟mark ℳ, :𝓟ma ℳ, :𝓟k 1.? poser le marqueur ℳ sur la première colonne de la dernière ligne de la plage 𝓟 optionnelle (par défaut la ligne courante)

Les marqueurs, équivalents donc des cavaliers en plastique jaune, ou des plots, ne sont par contre en aucun cas visibles… Il s’agit d’un/une tableau/liste interne de positions dans la session et sont utilisables avec les opérateurs1

(Neo)Vim et certaines implémentations rajoutent deux commandes de gestion sympatiques.

commande v.v pour…
:marks 1.? lister les marqueurs de la liste ℒ optionnelle (par défaut tous)
:delmarks ℒ, :delm 1.? supprimer les marqueurs de la liste ℒ obligatoire
:delmarks!, :delm! 1.? supprimer tous les marqueurs du tampon courant

La première, :marks va afficher un tableau des marqueurs utilisables dans le tampon (il peuvent avoir été définis par nous ou être des marqueurs internes) en quatre colonnes :

  1. Le caractère associé au marqueur, qui est aussi son nom ou la marque ℳ.
    La liste est triée lexicographiquement sur cette colonne.
  2. La ligne où ce marqueur est posé, en comptant à partir de un.
  3. La colonne où ce marqueur est posé, en comptant à partir de zéro…
  4. Le fichier (le nom du tampon) ou le début de la ligne (pour le tampon courant)

Il y a au moins deux marqueurs internes actifs.

:" 7.a afficher les marqueurs de fuchsia.txt
:marks
mark line  col file/text
 '      1    0
 "      1    0
 [      1    0
 ]      4    0 that. Them brought likeness multiply fifth us set. Fly.
Press ENTER or type command to continue
:" 7.b re-set des marqueurs dans fuchsia.txt
:delmarks!
:" 7.b re-lister les marqueurs de fuchsia.txt
:marks
mark line  col file/text
 '      1    0
 "      1    0
Press ENTER or type command to continue

Les marqueurs assignables par les usagers sont des lettres non accentuées. (Neo)Vim utilise quelques autres symboles (dont « ' » et « " » dans l’exemple), que nous n’allons pas détailler, et qui sont disponibles uniquement en utilisation pour des mouvements. Ces marqueurs :

  • sont propres à chaque tampon (i.e. chaque tampon a sa liste distincte),
  • sont mis à jour (plus précisément la position associée) lors des insertions de lignes,
  • sont restaurés lors des annulations de suppression par exemple,
  • disparaissent avec la suppression de la ligne associée, sauf exception.

On va distinguer deux2 sortes de marqueurs.

des marques locales

Dans la plupart des implémentations, ce sont les lettres minuscules « a » à « z » auxquelles (Neo)Vim ajoute la possibilité de redéfinir « ' ».

exo commande de l’exercice remarque ou message résultant variation de :marks3
7.c mz on pose le marqueur « zéro » +z 1 0
7.d jf.mu on passe à la seconde ligne et on pose le marqueur « un » sur le point +u 2 47
7.e yyp on recopie cette ligne en dessous : le marqueur « u » est toujours attaché à la seconde ligne et pour l’opération on a utilisé des marqueurs internes +[ 3 0, +] 3 69, +. 3 0
7.f kdd on supprime la ligne du marqueur « u » : celui-ci disparait de la liste et les autres sont mis à jour -u 2 47 et ~[ 2 0 et ~] 2 0 et ~. 2 0
7.g u on annule la suppression : le marqueur « u » est restauré… +u 2 47
7.h jdd on supprime la ligne qu’on avait ajoutée ~[ 3 0, ~] 3 0, ~. 3 0
7.i 0f.md on passe à la ligne suivante et on pose le marqueur « deux » sur le premier point de la ligne +d 3 6
7.j ;mt on pose le marqueur « trois » sur le point suivant de la ligne +t 3 12
7.k ;mq on pose le marqueur « quatre » sur le point suivant +q 3 21
7.l ;mc on pose le marqueur « cinq » sur le point suivant +c 3 26
7.m j0;ms on pose le marqueur « six » sur le premier point de la ligne suivante +s 4 4 that. Them brought likeness multiply fifth us set. Fly.
7.n 2;mh on pose le marqueur « huit » sur le dernier point +h 4 54
7.o ,ms on pose le marqueur « sept » sur l’avant-dernier point : remarquer que le marqueur a changé d’affectation (les noms sont à usage unique et on peut les déplacer) ~s 4 49
7.p 'u on se positionne au début de la ligne du marqueur « u »
7.q `q on se positionne à l’emplacement marqué « q » ~' 2 0
7.r d`c on supprime de la position du curseur à la position marquée « c » : le marqueur trace la même position dont le contenu a changé ~[ 3 21, ~] 3 21, ~. 3 21
7.s u restauration de la suppression ~[ 3 0, ~] 3 0, ~. 3 0
7.t :delmarks utc on supprime les marqueurs « c », « u », « t » : l’ordre importe peu et on peut séparer par des espaces -t 3 12, -u 2 47, -c 3 26
7.u :1d on supprime la première ligne : le marquer « z » est supprimé, et les autres numéros de lignes ajustés -z 1 0, ~' 2 21, ~d 2 21, ~h 3 54, ~q 2 21, ~s 3 49, ~[ 1 0, ~] 1 0, ~. 1 0
7.v :wn on enregistre et on passe au fichier suivant : nos marqueurs ne sont plus visibles ici…
7.w 3wms on défini un marqueur « s » trois mots plus loin +s 1 17
7.x :N retour sur le fichier initial (noter que le fichier n’est pas modifié) : notre marqueur « s » est là et différent
7.y :delm s pour en avoir le cœur net, supprimons le… -s 4 49
7.z :n repassons au vert (aucun des deux fichiers n’a été modifié) : son marquer à lui est toujours là…

des marques globales

La spécification dit que

Les implémentations doivent supporter les caractères minuscules de la locale POSIX donc non accentuées et plus précisément ASCII ainsi que l’apostrophe inversée et l’apostrophe dactylographique. Le support d’autres caractères est laissé à chaque implémentation.

Et là, Vim a fait un choix intéressant qui nous ramène au sujet de ce billet. Les lettres majuscules non accentuées sont partagées par tous les tampons… En plus de vingt-six marqueurs par tampon, on a en plus vingt-six marqueurs visibles de tous (raison pour laquelle je les appelle globaux et les autres locaux.)

exo commande de l’exercice remarque ou message résultant variation de :marks3
8.a f'mG marquer le premier apostrophe comme « Green » +G 1 49
8.b j;mV descendre sur la seconde ligne et marquer le premier apostrophe comme « Vert » +V 2 41
8.c G se mettre sur la dernière ligne (pour que :ls indique la troisième ligne)
8.d :n au suivant… (noter que c’est le nom du fichier qui est indiqué pour les marques « G » et « V »)
8.e j;mC descendre sur la seconde ligne et marquer le premier apostrophe comme « Cyan » +C 2 26
8.f k;mT passer sur la première ligne et marquer l’apostrophe comme « Turquoise » +T 1 35
8.g G passer au début de la dernière ligne (afin que :ls indique que c’est la ligne trois)
8.h :n au dernier… (même remarque pour les marques « C » et « T »)
8.i G;mB marquer l’apostrophe comme « Bleu » +B 3 16
8.j gg ici on veut la première ligne (pour :buffers)
8.k :rew retour sur fuchsia : on voit les marques en majuscule avec les noms des tampons
8.l 'V basculer sur vert.txt mais non sur la troisième ligne mais plutôt sur la deuxième
8.m `C basculer sur cyan.txt en position « 2,27 » et non « 3,1 »
8.n `G revenir sur vert.txt en position « 1,50 » et non « 2.1 »
8.o 'B et cette ligne de bleu.txt au lieu de la première ?
8.p :delm B on supprime le marqueur comme les autres -B 3 16
8.q :delm T comme c’est global on peut supprimer de n’importe où… -T 1 35
8.r :delm! on ne supprime que les marqueurs locaux du tampon -[ 1 0, -] 3 0
8.s :bp bah oui, :args nous dit que nos saut avec ' et ` n’affectent pas la liste d’arguments
8.t :delm! on ne supprime que les marqueurs locaux du tampon -[ 1 0, -] 3 0
8.u :bf on rembobine et on aligne les chakras
8.v :bdel 3 le fichier n’est plus dans la liste mais le marqueur C est toujours visible
8.w :bwip 3 maintenant tout ce qui y associé est retiré aussi… -C 2 26
8.x :bwip 3 E517: No buffers were wiped out
8.y :bdel 3 E516: No buffers were deleted
8.z :bunl 2

Voilà, ça fait un moyen de plus de parcourir les nombreux fichiers ouverts. Au passage, nous avons vu la différence entre :bdelete (qui supprime de la mémoire —comme :bunload— et de la liste) et :bwipeout (qui lance en plus le ramasse-miettes…)

des marques exclusives

(Neo)Vim offre la possibilité de conserver les marqueurs lors de certaines actions Ex.

commande v.v pour…
:lockmarks 𝓒, :lock 𝓒, :loc 𝓒 ?.? exécuter la commande Ex 𝓒 sans toucher au tableau/liste des marqueurs
:keepmarks 𝓒, :keep 𝓒, :kee 𝓒 ?.? exécuter la commande Ex 𝓒 (en fait que 𝓟! pour l’instant) en n’ajustant que les positions des marqueurs en dessous de cette plage 𝓟 par rapport au nombre de lignes en retour

Je les mentionne à titre informatif et nous n’allons pas plus nous en préoccuper.

historiques associées

On a vu que (Neo)Vim travaille avec des listes dans tous les sens. Elles sont utilisées aussi pour gérer les historiques valables tout au long de la session et indépendamment des tampons.

lignes de commandes

Il y a un historique distinct pour chaque type de ligne de commandes (recherche, commandes Ex, etc.) Les touches pour utiliser l’historique de la ligne de commande en cours sont :

touche v.v pour rappeler la plus…
?.? ancienne ligne correspondant au début de texte saisi
⇧↑, ?.? vieille ligne (i.e. le début) de l’historique
?.? récente ligne correspondant au début de texte saisi
⇧↓, ?.? récente ligne (i.e. la fin) de l’historique

(Neo)Vim permet d’utiliser, dans une ligne de commande en cours, les touches : «  », «  », «  » ou «  b », «  » ou «  e », «   » ou «   », «   » ou «   », «  » ou «  h », «  », la bascule «  ». Le comportement actuel vient essentiellement de la version 4.0 de Vim.

Il y a quelques commandes dédiées aux historiques.

commande v.v pour…
:history, :his ?.? afficher l’historique des dernières commandes
:history 𝓣 𝓟, :his 𝓣 𝓟 ?.? afficher la plage 𝓟 optionnelle (les dernières par défaut) l’historique de type 𝓣 optionnel (commandes par défaut) : cette seconde forme nécessite d’avoir au moins l’un des arguments optionnels
:keeppatterns 𝓒, :keepp 𝓒 8.0 exécuter la commande Ex 𝓒 sans mettre à jour l’historique de recherche

Le type d’historique, 𝓣, peut être donné par un mot clé réductible à son initial, ou par l’un des caractères utilisé pour l’invite de commande

type invite
cmd : commande Ex
search /, ? recherche
expr = registre d’expression
input @ saisie spécifique
debug > commande de débogage
all tout

La plage d’historique, 𝓟, est soit un entier pour indiquer le début, soit deux entiers séparés par une virgule pour indiquer « début,fin ». Ces entiers peuvent être sans signe pour indiquer la position absolue, ou être précédé du signe « - » pour indiquer le rang depuis la fin…

Les historiques sont affichés comme un tableau de deux colonnes :

  • Le numéro/rang de l’historique, aligné à droite sur sept colonnes.
    La dernière entrée est marquée par > en première colonne.
  • Le contenu de la ligne sans l’invite, aligné à gauche à partir de neuvième colonne.

La taille de l’historique est déterminée par l’option history qui a une valeur de 50 entrées par défaut.

exo commande de l’exercice remarque
9.a :his c voir l’historique des dernières commandes : on a fait history cmd
9.b /if chercher le prochain « if » (dans « Life »)
9.c 2n search hit BOTTOM, continuing at TOP
9.d ?ly seach hit TOP, continuing at BOTTOM
9.e :↑⌫s voir l’historique des dernières recherches : on a fait :history search
9.f :↑↑ 5,20 voir l’historique des commandes entre les rang 5 et 20

Il y a enfin la complétion de ligne sur laquelle on pourrait écrire toute une session, mais c’est pour une autre fois.

sauts et changements

Il y a deux autres historiques, actuellement de cent entrées, qu’il convient d’évoquer.

commande v.v pour…
:jumps, :ju ?.? afficher l’historique des sauts
:clearjumps, :cle 8.0 supprimer de l’historique les sauts liés à la fenêtre en cours
:changes ?.? afficher l’historique des changements de lignes
:keepjumps 𝓒, :keepj 𝓒 ?.? exécuter la commande Ex 𝓒 sans mettre à jour les listes de sauts et changements

Commençons par les sauts. Certaines commandes ont été classées historiquement comme étant des sauts (ou jumps en anglais) car déplaçant (presque toujours) le curseur plusieurs lignes au delà de sa position courante : « ' », « ` », « G », « / », « ? », « n », « N », « % », « ( », « ) », « [[ », « ]] », « { », « } », « :s », « :tag », « L », « M », « H », et toutes les commandes commençant l’édition d’un nouveau fichier.

La liste des sauts est affichée comme la liste des marqueurs, la première colonne (la marque) étant remplacée par le rang du saut, et la position courante est indiquée par > en première colonne.

:" 9.g afficher la liste des sauts
:" les sauts 3/2/1 sont respectivement pour : 9.b/9.c/9.d
:" les sauts 4/9 absents sont respectivement pour : 8.l/8.n
:jumps
 jump line  col file/text
  10     3    0 vert.txt
   8     1    0 bleu.txt
   7     1   49 vert.txt
   6     2    0 vert.txt
   5     3    0 bleu.txt
   3     3   38 that. Them brought likeness multiply fifth us set. Fly.
   2     2    9 itself. Life. Wherein. Was. It thing upon bearing it abundantly
   1     1   43 Whose whales bring moved female beginning fifth. Behold firmame
>
Press ENTER or type command to continue

Il est prévu des raccourcis pour revenir sur une des positions de liste, mais ce ne sont pas des mouvements (i.e. pas utilisable avec des opérateurs.) Le rang, 𝓝, utilisé avec ces combinaisons de touches, est toujours par rapport à la position courante > (qui est toujours de rang 0.)

touche v.v pour aller 𝓝 rang (optionnel, par défaut 1)…
𝓝⎈o ?.? avant (plus tôt/haut dans la liste)
𝓝⎈i, 𝓝 ou 𝓝 ou 𝓝 ?.? après (plus tard/bas dans la liste)

Fait curieux : Il semble que « o » a été choisi pour avoir « ancien/+vieux (soit older en anglais) » comme mnémonique ; mais à l’inverse, « n » pour « nouveau/+jeune (soit newer en anglais) » n’était pas possible… La touche la plus proche était la « i » et du coup ce n’est pas dans l’ordre sur les claviers qwerty/qwertz/azerty 🥲 Mais ça reste dans la même logique que les paires : « ( et ) », « { et } », « [[ et ]] », « < et > », en qwerty… (on retrouve la même chose pour la paire « , et ; » en azerty…)

exo commande de l’exercice remarque position courante d’après :jumps
9.h 2⎈o 2 9 fuchsia.txt
9.i 3⎈o 2 0 vert.txt
9.j ⎈i 3 0 bleu.txt
9.k '' on revient à la dernière marque automatiquement posée fuchsia.txt

C’est donc là un moyen pratique de se revisiter les positions remarquables des tampons déjà visité.

La liste des changements (ajouts/suppressions) fonctionne de manière similaire.

:" 9.l afficher la liste des modifs
:" sauf erreur, 1/2/3/4/5 sont respectivement pour 7.u/7.r,7.s/7.h/7.f,7.g/7.e
:changes
change line  col text
    5     3    0 itself. Life. Wherein. Was. It thing upon bearing it abundantl
    4     1    0 Whose whales bring moved female beginning fifth. Behold firmam
    3     3    0 itself. Life. Wherein. Was. It thing upon bearing it abundantl
    2     2    0 Whose whales bring moved female beginning fifth. Behold firmam
    1     1    0 Whose whales bring moved female beginning fifth. Behold firmam
>
Press ENTER or type command to continue

Il est prévu des combinaisons de touches pour revenir sur une des positions de liste, mais ce ne sont pas des mouvements (i.e. donc inutilisables avec des opérateurs.) Le rang, 𝓝, utilisé avec ces combinaisons de touches, est toujours par rapport à la position courante > (qui a toujours le rang 0.)

touche v.v pour aller 𝓝 rang (optionnel, par défaut 1)…
𝓝g; ?.? avant (plus tôt/haut dans la liste)
𝓝g, ?.? après (plus tard/bas dans la liste)

Ici on a repris la paire connue « ; et , » qui, contrairement aux apparences, opère (presque) avec la même logique que les paires « f et F » ainsi que « / et ? » en qwerty. (par contre c’est l’autre logique en azerty…) 😀

exo commande de l’exercice remarque position courante d’après :changes
9.m 4g; 1 0 fuchsia.txt
9.n g; 3 0 fuchsia.txt
9.o 3g, 2 0 fuchsia.txt
9.p g, 1 0 fuchsia.txt
9.q dd fuchsia.txt
9.r '' fuchsia.txt

À noter que, pour des changements proches sur une même ligne, il n’y a qu’une seule entrée avec la dernière colonne. C’est contrôlé par l’option textwidth …qui par défaut est 0, ce qui désactive l’effet…

exo commande de l’exercice remarque position courante d’après :changes
9.s 2wdw g; 1 13 fuchsia.txt
9.t e dt. g; 1 18 fuchsia.txt
9.u :qa!

C’est tout pour cette fois. さようなら


  1. Si, si, vous savez ; ce sont les commandes qui indiquent une action et nécessitent d’être doublée pour prendre la(s) ligne(s) entière(s) ou d’être suffixée par un mouvement qui indique la portée. « change », « delete », « yank », « move > (indent) », « move < (unindent) », « ! (though shell filter) »
  2. Trois en fait, mais on n’en parlera que de deux ici.
  3. Ici, l’affichage exact ne sera pas respecté et on omettra la ligne de texte et les champs seront séparés par une espace ; et précèdera de :
    — + pour indiquer l’apparition dans la liste,
    — - pour indiquer la disparition de la liste,
    — ~ pour indiquer le changement dans la liste.

J’espère qu’après cette lecture pratique que :

  • ça ne fait plus peur de se retrouver avec un tas de fichiers chargés 😉
  • l’on sait comment chercher (et remplacer) dans plusieurs fichiers ouverts 😁
  • l’on n’a besoin d’aucun plugin pour s’y retrouver dans les tampons 😊

Petit sondage maintenant : quels sont, parmi les commandes découvertes, vos trois nouveaux favoris ?

Aucun commentaire

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte