Salut,
J'écris beaucoup de documents à base de fichiers markdown assemblés par un Makefile qui génère ensuite un pdf (principalement avec pandoc).
Pour découper le document, j'ai pas mal de fichiers par chapitres, annexes, …
Pour modifier ou chercher dans le document il faut donc que j'ouvre chaque fichier indépendamment et je me retrouve avec un tas d'onglets et de buffers séparés qui ne facilite pas la recherche et l'édition pour la correction.
Je me demandais s'il n'existe pas une solution pour ouvrir tous les fichiers dans un même buffer tout en gardant le découpage des fichiers bien sûr ?
Si jamais quelqu'un a une idée ça m’intéresse.
thx
# des listes et grep
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 10.
Autant tu as besoin d’ouvrir les fichiers pour les modifier, autant ce n’est pas nécessaire pour y chercher :
vi(dontvimest une évolution) est prévu pour travailler avec le shell…!Ici, j’utilise
*pour dire tous les fichiers du répertoire courant (le répertoire de travail, celui dans lequel tu as lancé l’éditeur …à ne pas confondre avec le répertoire effectif du fichier en cours d’édition ou autres emplacements.) On peut bien entendu spécifier tout autre chemin qui sera compris du shell (dans ton cas, je pense que ce serait à minima*.mdsi tes fichiers markdown utilisent cette extension.)Ceci va juste afficher le résultat de la recherche, avec le message « Press ENTER or type command to continue » (traduit dans la langue configurée…) Pour plusieurs opérations de suite, on peut temporairement revenir au shell en mettant l’éditeur en pause en arrière plan avec Ctrl+z ou (ça fait la même chose, sous Unix-like)
ou (c’est un synonyme plus évocateur)
Traditionnellement, il est possible d’ouvrir un sous-shell (c’est le comportement de Ctrl+Z sur la plupart des plateformes non Unix-like) avec
(qui dit sous-shell dit que l’on revient à l’éditeur quand on termine ce shell, normalement/habituellement par
exitou équivalent.)Les éditeurs modernes permettent d’avoir un/une encart/zone (un/une cadre/vue au sens de Vim) de terminale. C’est aussi possible avec NeoVim et Vim ≥ 8.1
(en fermant le terminal, par
exitou équivalent, on ferme le/la cadre/vue associée aussi.)Cette dernière possibilité est considéré comme une « super fonctionnalité » (i.e. killer feature dans la langue de Shakespeare) par les gens qui l’utilisent mais c’est un autre débat.
Avec un sous-shell ou un terminal, ou en revenant temporairement dans son shell, on peut faire nos modification avec les outils classiques…
Mais je comprends que ce soit moins sexy que d’opérer directement depuis l’éditeur, quoique.
Si au lieu de lister les fichiers tu veux plutôt les ouvrir, il fallait traditionnellement le faire depuis le shell (en passant la liste des fichiers à ouvrir à
viainsi)Même remarque pour
*ici (qui devrait être*.mdou autre que je te laisse adapter)Ici, il est important de faire une petite mise au point : on crée (originellement) autant de tampons que de fichiers. Du coup un peu normal et inévitable
Beaucoup ne le savent pas, mais c’est géré de base (à vérifier mais il me semble que c’est dans POSIX, en tout cas c’est implémenté dans
nvi) ; Pour naviguer dans la liste de fichiers c’est aussi simple que ::nextpour basculer sur le fichier suivant de la liste:previouspour basculer sur le fichier précédent de la liste:first(ou:rewind) pour se positionner sur le premier fichier de la liste:lastpour se positionner sur le dernier fichier de la listeVim et ses dérivés rajoutent :
:Nextpour basculer sur le fichier précédent de la liste (dansnvi,:Nextet:Previouscréent un/une cadre/vue en dessous et y ouvre le tampon ; dansvim, on a repris la similitude des commandesnetN…):argspour lister les fichiers avec le fichier courant entre crochets:Nargeditou:Nargumentpour basculer sur le fichier en positionN:argeditF pour basculer sur le fichier nomméF:NargaddF pour ajouter le fichierFen positionN(optionnel, par défaut après fichier courant):Pargdeletepour retirer les fichiers en positionP(en fait c’est une plage au sensex… et c’est la position actuelle par défaut):argdeleteF pour retirer les fichiers correspondant au motifF:wpreviousou:wNext, et:wnext, font un:w(et on peut indiquer un fichier alternatif) puis la commande (c’est dans le même esprit que:wq):PargdoX pour appliquer une commandeX(au sensex… sans les deux points) aux fichiers en positionP(c’est en fait une plage au sensex… et c’est tous par défaut)Ce dernier va t’intéresser car si tu veux faire le remplacement du mot « truc » par « machin » dans tous ces fichiers ouverts, ça revient donc à l’incantation suivante :
Vim a une subtilité (que je n’ai pas listé plus haut et) que tu peux utiliser avant de lancer ton incantation : il est possible de modifier la liste d’arguments …en argument de
args…ou en jouant avecargadd etargdelete si on préfère. Bien entendu, cela impacte toutes les autres commandes qui se basent sur cette liste. Ainsi, par exemple avant de faire leargdoprécédent, on peut restreindre la liste (et se positionner sur le premier de la nouvelle liste) des fichiers par (cela accepte les globs du shell)Une autre précision s’impose. J’avais laissé entendre que tous les fichiers sont chargés en mémoire. Il s’agit là des implémentations naïves, mais en pratique on va travailler avec une liste chainée de tampons sur laquelle on va « cycler » et éviter ainsi de saturer la mémoire. 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.)
Vim et d’autres vont plus loin que les codes historiques et distinguant la liste d’argument (dont je viens de parler) de la liste de tampons. Il y a donc d’autres commandes pour naviguer dans cette seconde liste et c’est tout aussi simple que
:bnextpour basculer sur le tampon suivant de la liste:bprevious(ou:bNext) pour basculer sur le tampon précédent de la liste:bfirst(ou:brewind) pour se positionner sur le premier tampon de la liste:blastpour se positionner sur le dernier tampon de la liste:buffers(ou:filesou:ls) pour lister les tampons dans quatre colonnes : rang, statuts, fichier, dernière position:bufferN pour basculer sur le tampon numéroN:bufferF pour basculer sur le tampon nomméF:bmodifiedpour basculer sur le tampon suivant ayant été modifiéJusque là ça va… On peut manipuler aussi cette liste, sauf que là on agit effectivement sur les fichiers manipulés (contrairement à la liste d’arguments qui est décorrélée une fois l’éditeur lancé et les fichiers chargés —les deux listes sont donc égales au départ)
:baddF (ou:baltF ?) pour ajouter le fichier F à la liste(mais comme on fonctionne en mode un peu « lazy », il ne sera chargé en mémoire que lorsqu’on en aura vraiment besoin.)
:editF n’est pas propre àvim(c’est requis par POSIX) et sert à remplacer le tampon courant par celui du fichierFoptionnel (quand aucun chemin n’est indiqué ça recharge le même tampon):Nbdelete(ou:bdeleteN) pour retirer le(s) tampon(s) en position N (la seconde forme permet d’indiquer plusieurs positions séparées par des blancs, la première forme peut être une plagedébut,fininclus) de la liste.:bdeleteF pour retirer le fichierFde la liste.bdeletefonctionne en « lazy » : la mémoire utilisée n’est pas libérée immédiatement, et certaines données associées sont gardées actives (c’est bien utile la plupart du temps.) Il faut remplacer parbwipeoutà la place pour effectivement tout virer, etbunloadà la place pour au contraire retirer de la mémoire mais pas de la liste. Ce sont des subtilités d’usage avancé qu’on a rarement besoin de connaitre.:PbufdoX pour appliquer une commandeX(toujours au sensex… sans les deux points) aux fichiers en positionP(toujours une plage au sensex… et c’est tous par défaut)C’est ici que le fait d’avoir deux listes fait sens : si on veut agir sur tous les fichiers en cours de travail, alors
bufdo; sinon on peut jouer avec une liste d’arguments aux oignons puisargdo; et ce n’est pas tout… Ceci dit, il faut une certaine pratique et de la rigueur pour ne pas se mélanger les pinceaux entre ces deux listes.Les commandes de navigation dans les tampons ont des équivalents, préfixé par
spour en plus créer un/une cadre/vue avant de faire la bascule demandée ::sbuffer,:sbnext,:sbNext,:sbprevious,:sbrewind,:sbfirst,:sblast,:sbmodified.À cela s’ajoute deux commandes qui vont réorganiser l’écran pour afficher chaque tampon dans un/une cadre/vue …avec un maximum de
Ncadres/vues si mentionné (cet argument reste optionnel donc) ::unhideN (ou:sunhideN qui est synonyme) pour les tampons chargés en mémoire:ballN (ou:sballN qui est synonyme) pour tous les tampons de la listePrécision de vocabulaire : j’écris tout le temps « un/une cadre/vue » parce-que dans l’univers
viquand on parle de fenêtre (ou window en anglais), il ne s’agit pas des feuilles des interfaces graphiques mais bien de la portion d’écran —que je préfère appeler vue/cadre (ou frame/view en anglais)— qui affiche une portion du tampon… On peut partager (soit split en anglais) l’écran —ou le cadre courant— horizontalement ou verticalement, et chaque zone/cadre peut afficher un tampon différent …ou pas (on peut très bien avoir plusieurs vues différentes sur un même tampon, c’est le cas quand on fait un diff graphiquement…)Pour naviguer dans les cadres/vues, il faut utiliser une séquence de touches commençant par Ctrl+w (à l’usage, c’est plutôt plus efficace que ça n’y parait) :
vi) : pour se rendre le/la cadre/vue qui suit deNrangs (optionnel et vaut un par défaut) dans la listenetN) : pour se rendre sur le/la cadre/vue qui précède deNrangs (optionnel et vaut un par défaut) dans la listeLes cadres/vues sont numérotés de haut en bas et de gauche à droite, du plus bas niveau au plus haut niveau… En général on s’en sort facilement si on ne fait pas quelque chose de trop compliqué…
Pour la création, on a trois cas prévus :
:split, pour partager horizontalement le/la cadre/vue en cours en deux partie égales et charger le même tampon dans la nouvelle zone (au dessus) et s’y positionner.:new, pour partager horizontalement le/la cadre/vue en cours en deux partie égales et se positionner dans la nouvelle zone (au dessus) vide.:vsplit, pour partager verticalement le/la cadre/vue en cours en deux partie égales et charger le même tampon dans la nouvelle zone (à gauche) et s’y positionner.:vnew, pour partager verticalement le/la cadre/vue en cours en deux partie égales et se positionner dans la nouvelle zone (à gauche) vide.La forme
expermet d’indiquer en suffixe un chemin de fichier à associer au nouveau cadre, et en préfixe la dimension (nombre de lignes horizontalement ou nombre de colonnes verticalement) au lieu de faire fifty-fifty.Pour la fermeture, il y a trois cas aussi :
:quit, pour fermer le/la cadre/vue en cours, et le programme (ou l’onglet) si on ferme le/la seul/seule qu’il reste.:close, pour fermer le/la cadre/vue en vue en cours, sauf si c’est le/la seul/seule de la liste.:only, pour fermer tous/toutes cadres/vues sauf celui/celle en cours.Il est prévu aussi de pouvoir les redimensionner ou les réorganiser. Mais venons au plus important ici :
:PwindoX permet d’envoyer la commandeX(toujours au sensexet sans les deux-points) à tous les cadres. Elle est pas belle la vie ?Tu utilises probablement
gvimavec une configuration pour faire comme les autres éditeurs en associant un fichier à un onglet. En fait, Vim et ses dérivés sont plus puissants/subtiles : un onglet (ou tab en anglais) est en fait un groupe de cadres/vues… Là où les autres éditeurs utilisent les onglets (ou une liste de fenêtres) pour donner accès aux tampons, il s’agit dans Vim d’espaces de travail que je comparerais à des branches Git.Bien qu’on ne sache pas afficher plusieurs onglet en console (plus précisément dans le l’interface austère héritée de
visinon on sait faire des interfaces graphiques en console, comme celui de MC ou MinEd ou LE etc.) Vim et ses dérivés savent gérer ces groupes en CLI (c’est un poil ardu sans repère visuel, mais:set showtablineest ton amie.)La navigation dans les groupes (ou page-tabs dans la documentation de Vim) se fait de manière commune aux tampons.
:tabnextpour basculer sur l’onglet suivant de la liste:tabprevious(ou:tabNext) pour basculer sur l’onglet précédent de la liste:tabfirst(ou:tabrewind) pour se positionner sur le premier onglet de la liste:tablastpour se positionner sur le dernier onglet de la liste:tabspour lister les ongletsPour la gestion des onglets, les commandes sont simples aussi et se rapprochent plus de celles des cadres.
:Ptabcloseou:tabcloseP ferme l’onglet en positionPoptionnelle (par défaut l’onglet courant):Ptabonlyou:tabonlyP ferme tous les onglets sauf celui en positionPoptionnelle (par défaut l’onglet courant):Ntabmoveou:tabmoveN déplace l’onglet courant après celui du rangNoptionnel (par défaut le dernier):tabmove±N déplace l’onglet courant de ±Nrang par rapport à sa position actuelle:NtabeditF ou:NtabnewF ouvre un nouvel onglet après celui de rangNoptionnel (par défaut l’onglet courant) et y charge le fichierFoptionnel (quand on n’indique pas de fichier on a un onglet vide.):PtabdoX enfin, permet d’appliquer la commandeX(toujours au sensexet toujours sans les deux-points) à l’onglet de rangP(en fait une plageex) optionnel (par défaut tous les onglets)Bref, plusieurs pistes selon ton choix d’organisation.
Vim et NeoVim ont une notion intéressante que l’on peut mettre à profit : les « quick fix » ! C’est une fonctionnalité que l’on trouve dans beaucoup d’EDI, mais ici c’est ouvert (donc adaptable à d’autres langages de programmation et pouvant être détourné pour d’autres usages comme ici.) L’idée, empruntée au compilateur Aztec C, est de capturer les messages d’erreur (de compilation) et de sauter aux emplacements pointés. Il faut bien entendu indiquer comment décortiquer ces messages (de compilation), et ça se fait en déclarant
errorfile; mais nous n’avons pas besoin de le faire car la forme « fichier:ligne:message » fait partir des formats reconnus par défaut.Au lancement, l’éditeur ouvre le premier fichier et se positionne sur la première ligne incriminée pour permettre de faire les corrections. Il faudra ensuite sauter aux autres lignes incriminées pour faire les corrections qui vont bien.
:Ncnextpour sauter deNoptionnelles (par défaut 1) rang en avant:Ncpreviousou:NcNextpour sauter deNoptionnelles (par défaut 1) rang en arrière:crewindN pour sauter au rangNoptionnel (par défaut 1) exactement:cfirstpour sauter au premier rang:clastpour sauter au dernier rang:clistpour afficher la liste utilisée (ici, ce sera notre fichier préfixé des rangs utilisés un peu commecat -n /tmp/resultats.listounl /tmp/resultats.list)On remarque que la dernière ligne affiche (je suis en anglais) « (rang-courant of lignes-total): message » (dans notre cas le message est juste la ligne grepée et ça reste utile pour comparer visuellement avec la ligne modifiée dans le tampon.)
On remarque aussi que lorsqu’on avance ou recule dans la liste (d’erreurs), les fichiers sont chargés automatiquement (avec ajout dans la liste des tampons évoqué plus tôt.) Si l’on veut se restreindre au tampon en cours, pas besoin de compter et surveiller ; il suffit, sous réserve que la liste soit triée (par tampon et par ligne et colonne), de :
:Ncnextpar:Ncafter(après/suivant) ou par:Ncbellow(dessous):Ncprevious/cNextpar:Ncbefore(avant) ou par:Ncabove(dessus)Quand on a réalisé toutes les modifications dans le tampon courant, pas besoin de compter et surveiller pour passer à un tampon voisin ; il suffit, sous réserve que la liste soit triée (par tampon et par ligne et colonne), d’utiliser
:Ncnfilepour sauter à la première ligne fautive dans leNième tampon après celui courant (optionnel, par défaut le suivant), et si on est déjà sur le dernier alors laNième ligne fautive (par défaut la suivante):Ncpfileou:NcNfilepour sauter à la première ligne fautive dans leNième tampon avant celui courant (optionnel, par défaut le précédent), et si on est déjà sur le premier alors laNième ligne fautive (par défaut la précédente)Quand la correction est une action simple, on peut la répéter à un autre endroit fautif en utilisant
.Pour des actions plus complexes, ne pas oublier qu’il y a la possibilité d’enregistrer et utiliser des macros.Quand la correction est la même partout, pas besoin de travailler interactivement ; on a encore la possibilité de passer une commande
X(toujours au sensex) à une plageP(toujours au sensex) de la liste ::PcdoX pour agir sur les lignes incriminées(on fait un
:cfirst, puis on boucle avec la paire:X et:cnext):PcfdoX pour agir sur les fichiers incriminés(on fait un
:cfirst, puis on boucle avec la paire:X et:cnfile)Cela se traduit, avec notre même exemple, par :
(on fait un
updateà chaque modification de ligne et non en changeant de tampon, mais je ne sais pas comment faire mieux) ou par :Comme les autres listes déjà rencontrées, il y a d’autres commandes pour la manipuler.
Pour commencer, on peut réduire la liste en utilisant un plugin natif (mais inactif par défaut) et un motif à la Vim…
:Cfilter /à-garder/:Cfilter! /à-virer/Ainsi par exemple, si on ne veut pas que notre remplacement s’applique aux lignes de la liste (cela s’applique donc indifféremment aux chemins de fichiers et aux messages) qui ne contiennent pas le mot “bidule”, il faut au préalable réduire la liste avec :
Pour poursuivre, il n’est pas nécessaire de lancer l’éditeur avec la quick list (option
-q) ; on peut charger le fichier dynamiquement.:cfileF pour construire la liste avec le contenu deFpuis se positionner sur la première ligne pointée:cgetfileF pour construire la liste avec le contenu deFmais ne saute pas à la première ligne incriminée:caddfileF pour augmenter la liste avec le contenu deFDu coup, ce qui a été fait depuis le shell peut se traduire directement dans l’éditeur par
Il n’est pas nécessaire de passer par un fichier
Fsi la liste d’erreurs est déjà chargé dans un tamponN(optionnel, par défaut le tampon courant) ::cfileF par:cbufferN:cgetfileF par:cgetbufferN:caddfileF par:caddbufferNDu coup, l’exemple précédent peut se traduire aussi par (en commençant par créer un nouveau tampon non nommé)
Pour les usagers avancés, il est possible de faire la même chose en utilisant des expressions (résultats de commandes
ex.) Mais passons rapidement au meilleur réservé pour (presque) la fin ::grepA pour construire la liste en capturant la sortie de la commandegrepavec les argumentsA(motif fichiers):grepaddA pour compléter la liste avec la capture de la sortie de la commandegrepavec les argumentsA(motif fichiers)Vim a donc prévu ce cas particulier… qui est un besoin assez courant…
Notre exemple précédent devient simplement…
Par défaut, la commande appelée est bien le vénérable
grep -n $* /dev/null. Si l’on veut utiliser une autre commande (par exempleag—The Silver Searcher—,pt—The Platinium Searcher—, Nirw Search,ripgrep, etc.) ou passer des options, il faut positionnergrepprg. Si le format de sorti est différent (par défaut c’est actuellement la liste%f:%l:%m,%f:%l%m,%f %l%m) il faut positionnergrepformat(c’est utilisé uniquement pour ce cas et c’est pratique pour utiliser la fonctionnalité sans impactererrorformatet combiner avec d’autres fonctionnalités)Par exemple, pour rendre mon motif insensible à la casse, notre même exemple devient
Deux derniers trucs pour la route.
Quand on charge une nouvelle liste ou filtre une existante, les anciennes ne sont pas perdues… Vim utilise une liste d’historique circulaire de dix entrées à laquelle on a accès.
:Nchistoryaffiche la liste d’historique, et si on indique un rangNoptionnel alors celui-ci devient la liste courante…:cnewerN fait de la liste deNrang après (optionnel, par défaut la suivante) la liste courante.:colderN fait de la liste deNrang avant (optionnel, par défaut la précédente) la liste courante.C’est une facilité utile quand on commence à utiliser intensivement la fonctionnalité.
L’autre truc qui peut être sympa est que l’on peut ouvrir la liste dans un/une cadre/vue, mais contrairement à
:clist, il n’y a plus les rangs (juste le contenu.):copenN crée un/une cadre/vue (horizontale en bas ou verticale à gauche) de hauteur ou largeurNoptionnelle (par défaut dix lignes.)Il ne peut y avoir qu’une seule zone, donc la commande bascule vers celle existante (et la redimensionne si un taille est spécifiée) si on la lance plusieurs fois.
Il demeure possible d’utiliser les commandes habituelles pour redimensionner ou déplacer cette zone spéciale.
:ccloseferme le/la cadre/vue.C’est une zone spéciale donc la commande ne s’applique jamais aux autres. Par contre,
:close ou:quit s’applique sans souci.:cwindowN est presque pareil à:copenN excepté que lorsque la liste est vide ça fait un:cclose:cbottombascule dans le/la cadre/vue spéciale et se positionne à la fin, ce qui est utile quand la liste est modifiée programmatiquement de manière asynchrone.Il n’y a certes pas les rangs affichés, mais la ligne courante est mise en évidence, et en appuyant sur la touche Enter (ou en cliquant) sur n’importe quelle ligne permet de charger le tampon (si ça ne l’était pas) et de se positionner sur la ligne. Si besoin, on peut utiliser plutôt Ctrl+Enter pour créer un/une autre cadre/vue.
Par exemple, dans le cadre Markdown, on peut s’en servir pour la table des matières :
(Je n’ai pris en compte qu’une des deux formes de titraille et les lignes commençant par le croisillon dans les blocs de code ou les lignes commentées ne sont pas exclues.)
Il y a d’autres listes, dont celle dite location que je ne trouve pas pertinent ici.
Comme promis, c’est la fin du voyage. Et je me rends compte que cette réponse aurait pu être un journal…
Il y a certainement d’autres approches, seule l’imagination est la limite avec Vim :D
Il est possible aussi que quelqu’un quelque part a pondu un plugin qui répond avec une certaine simplicité et élégance à ta problématique. Me satisfaisant des fonctionnalités natives, je n’ai jamais fouillé.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: des listes et grep
Posté par Xavier Maillard . Évalué à 2.
De passage sur le forum, je lis la question et je vois cette réponse, oh my ! ça c'est une réponse étoffée.
Bravo et merci
[^] # Re: des listes et grep
Posté par gUI (Mastodon) . Évalué à 5.
Oui, s'il-te-plaît !!!
En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.
[^] # Re: des listes et grep
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 4.
Ce sera 3 journaux du coup. Le premier est livré.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: des listes et grep
Posté par martoni (site web personnel, Mastodon) . Évalué à 3.
Wow, la réponse de dingue !
Je viens de la voir.
Merci.
Entre temps je m'étais fait un petit utilitaire python pour merger puis splitter et mettre à jour les fichiers : classeur.
Il «faut» en faire un journal !
J'ai plus qu'une balle
[^] # Re: des listes et grep
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
Je vais regarder ça. Sinon, comme tu l’auras deviné, je fais merge et split directement en ligne de commande ou dans Vim (ça en fait des journaux potentiels à écrire haha.)
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: des listes et grep
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3.
C’est fait, au tiers.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.