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
(dontvim
est 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*.md
si 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
exit
ou é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
exit
ou é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 à
vi
ainsi)Même remarque pour
*
ici (qui devrait être*.md
ou 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 ::next
pour basculer sur le fichier suivant de la liste:previous
pour basculer sur le fichier précédent de la liste:first
(ou:rewind
) pour se positionner sur le premier fichier de la liste:last
pour se positionner sur le dernier fichier de la listeVim et ses dérivés rajoutent :
:Next
pour basculer sur le fichier précédent de la liste (dansnvi
,:Next
et:Previous
créent un/une cadre/vue en dessous et y ouvre le tampon ; dansvim
, on a repris la similitude des commandesn
etN
…):args
pour lister les fichiers avec le fichier courant entre crochets:
Nargedit
ou:
Nargument
pour basculer sur le fichier en positionN
:argedit
F pour basculer sur le fichier nomméF
:
Nargadd
F pour ajouter le fichierF
en positionN
(optionnel, par défaut après fichier courant):
Pargdelete
pour retirer les fichiers en positionP
(en fait c’est une plage au sensex
… et c’est la position actuelle par défaut):argdelete
F pour retirer les fichiers correspondant au motifF
:wprevious
ou: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
):
Pargdo
X 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 avecarga
dd etargd
elete 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 leargdo
pré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
:bnext
pour 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:blast
pour se positionner sur le dernier tampon de la liste:buffers
(ou:files
ou:ls
) pour lister les tampons dans quatre colonnes : rang, statuts, fichier, dernière position:buffer
N pour basculer sur le tampon numéroN
:buffer
F pour basculer sur le tampon nomméF
:bmodified
pour 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)
:badd
F (ou:balt
F ?) 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.)
:edit
F n’est pas propre àvim
(c’est requis par POSIX) et sert à remplacer le tampon courant par celui du fichierF
optionnel (quand aucun chemin n’est indiqué ça recharge le même tampon):
Nbdelete
(ou:bdelete
N) 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,fin
inclus) de la liste.:bdelete
F pour retirer le fichierF
de la liste.bdelete
fonctionne 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.:
Pbufdo
X 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
s
pour 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
N
cadres/vues si mentionné (cet argument reste optionnel donc) ::unhide
N (ou:sunhide
N qui est synonyme) pour les tampons chargés en mémoire:ball
N (ou:sball
N 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
vi
quand 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 deN
rangs (optionnel et vaut un par défaut) dans la listen
etN
) : pour se rendre sur le/la cadre/vue qui précède deN
rangs (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
ex
permet 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 :
:
Pwindo
X permet d’envoyer la commandeX
(toujours au sensex
et sans les deux-points) à tous les cadres. Elle est pas belle la vie ?Tu utilises probablement
gvim
avec 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
vi
sinon 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 showtabline
est ton amie.)La navigation dans les groupes (ou page-tabs dans la documentation de Vim) se fait de manière commune aux tampons.
:tabnext
pour 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:tablast
pour se positionner sur le dernier onglet de la liste:tabs
pour lister les ongletsPour la gestion des onglets, les commandes sont simples aussi et se rapprochent plus de celles des cadres.
:
Ptabclose
ou:tabclose
P ferme l’onglet en positionP
optionnelle (par défaut l’onglet courant):
Ptabonly
ou:tabonly
P ferme tous les onglets sauf celui en positionP
optionnelle (par défaut l’onglet courant):
Ntabmove
ou:tabmove
N déplace l’onglet courant après celui du rangN
optionnel (par défaut le dernier):tabmove
±N déplace l’onglet courant de ±N
rang par rapport à sa position actuelle:
Ntabedit
F ou:
Ntabnew
F ouvre un nouvel onglet après celui de rangN
optionnel (par défaut l’onglet courant) et y charge le fichierF
optionnel (quand on n’indique pas de fichier on a un onglet vide.):
Ptabdo
X enfin, permet d’appliquer la commandeX
(toujours au sensex
et 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.
:
Ncnext
pour sauter deN
optionnelles (par défaut 1) rang en avant:
Ncprevious
ou:
NcNext
pour sauter deN
optionnelles (par défaut 1) rang en arrière:crewind
N pour sauter au rangN
optionnel (par défaut 1) exactement:cfirst
pour sauter au premier rang:clast
pour sauter au dernier rang:clist
pour afficher la liste utilisée (ici, ce sera notre fichier préfixé des rangs utilisés un peu commecat -n /tmp/resultats.list
ounl /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 :
:
Ncnext
par:
Ncafter
(après/suivant) ou par:
Ncbellow
(dessous):
Ncprevious
/cNext
par:
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
:
Ncnfile
pour sauter à la première ligne fautive dans leN
ième tampon après celui courant (optionnel, par défaut le suivant), et si on est déjà sur le dernier alors laN
ième ligne fautive (par défaut la suivante):
Ncpfile
ou:
NcNfile
pour sauter à la première ligne fautive dans leN
ième tampon avant celui courant (optionnel, par défaut le précédent), et si on est déjà sur le premier alors laN
iè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 ::
Pcdo
X pour agir sur les lignes incriminées(on fait un
:cfirst
, puis on boucle avec la paire:
X et:cnext
):
Pcfdo
X 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.:cfile
F pour construire la liste avec le contenu deF
puis se positionner sur la première ligne pointée:cgetfile
F pour construire la liste avec le contenu deF
mais ne saute pas à la première ligne incriminée:caddfile
F pour augmenter la liste avec le contenu deF
Du 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
F
si la liste d’erreurs est déjà chargé dans un tamponN
(optionnel, par défaut le tampon courant) ::cfile
F par:cbuffer
N:cgetfile
F par:cgetbuffer
N:caddfile
F par:caddbuffer
NDu 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 ::grep
A pour construire la liste en capturant la sortie de la commandegrep
avec les argumentsA
(motif fichiers):grepadd
A pour compléter la liste avec la capture de la sortie de la commandegrep
avec 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 impactererrorformat
et 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.
:
Nchistory
affiche la liste d’historique, et si on indique un rangN
optionnel alors celui-ci devient la liste courante…:cnewer
N fait de la liste deN
rang après (optionnel, par défaut la suivante) la liste courante.:colder
N fait de la liste deN
rang 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 à
:cl
ist, il n’y a plus les rangs (juste le contenu.):copen
N crée un/une cadre/vue (horizontale en bas ou verticale à gauche) de hauteur ou largeurN
optionnelle (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.
:cclose
ferme le/la cadre/vue.C’est une zone spéciale donc la commande ne s’applique jamais aux autres. Par contre,
:cl
ose ou:q
uit s’applique sans souci.:cwindow
N est presque pareil à:copen
N excepté que lorsque la liste est vide ça fait un:cclose
:cbottom
bascule 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.