NVim persistence: le retour des sessions
Table des matières
Cela fait des années que j’utilise les Sessions, qu’elles soient de tmux
ou de vim
puis désormais neovim
. Elles permettent de conserver l’état de mon activité d’un jour à l’autre, d’un redémarrage ou d’une déconnexion à l’autre.
Mon éditeur de texte favoris a une commande pour sauvegarder son état: :mksession<CR>
et une pour le restaurer: nvim -S
. Mais bon, devoir se souvenir de sauvegarder, c’est tout de même une bonne façon d’oublier. Donc, j’ai utilisé le plugin Obsession
du merveilleux Tim POPE et ce pendant de nombreuses années.
Mais aujourd’hui… Tout a changé !
Coucou Evan 👋 Merci de m’avoir motivé à écrire cet article !
Au début, il n’y avait rien
Oui, tout a changé, quand je me baladais naïvement sur reddit, en quête de rien. Sans crier gare, je tombe sur l’annonce de lazy.nvim
, un nouveau gestionnaire de plugins.
Même si je suis pleinement content de packer
pour ce travail, je devais satisfaire ma curiosité: le contenu est bon mais ce qui m’interroge le plus, c’est l’auteur dudit plugin: u/folke
. Il a l’air d’être connu comme le loup blanc, c’est le taulier, tout le monde salue son travail. Et j’avoue que je suis un peu vexé, car je traine mes guêtres dans vim
/nvim
depuis pas mal de temps sans connaître l’énergumène.
Un petit tour sur son profil github
m’apprends que le bonhomme a effectivement tout d’une sommité et je continue sur la liste de ses plugins les plus côtés pour tomber sur persistence
.
Puis Folke
créa Persistence
Persistence is a simple lua plugin for automated session management.
Il le dit, et il le fait ! Mais encore plus simplement et sensiblement mieux que Obsession
.
Simple
Un des côtés négatifs de Obsession
est qu’il faut penser à le démarrer: :Obsession<CR>
. Sans ça, la session n’est pas mémorisée.
Là où Persistence
se contente de démarer silentieusement au dès lors qu’un buffer est écrit sur disque. Alors, oui, c’est fancy, il y a du lazy loading, toussa toussa… N’empêche que ça fait le café tout seul et c’est une occasion de moins de se planter.
Mieux
Obsession
repose sur les :h :mksession
natives de vim
/nvim
. Et, par défaut, elles écrivent l’état dans un fichier Session.vim
, là où la commande a été invoquée.
Il est possible de spécifier un chemin pour les ranger “ailleurs”, loin du code. Mais ensuite, il faut retourner les chercher, se souvenir de “laquelle est laquelle” etc. C’est une activité à laquelle je me suis adonnée un bout de temps avant de finalement laisser les Session.vim
sur place et simplement lancer (n)vim -S
en me prenant un message d’erreur si aucune session n’est présente. (En réalité j’ai écrit une fonction bash
qui ajoute ou non le -S
si session il y a mais là n’est pas la question.)
Persistence
quant-à-lui s’occupe déjà de tous ces problèmes: il y a un champ dir
dans sa configuration pour indiquer où ranger les sessions et ne plus avoir à s’en occuper. Et la commande require("persistence").load()
va chercher la session la plus récente utilisée dans ce dossier. Que de soucis en moins !
Bonus
Avec les copains geeks (👋 Yannick, 👋 Pierre-Luc 👋), on parlait des folds. Les folds permettent de cacher des parties du code qui ne nous importent que peu la plupart du temps. Un exemple typique est l’implémentation d’un trait en Rust 🦀 : un fold nous informe du nom du trait et du type pour lequel il est implémenté ; C’est amplement suffisant, pas besoin d’avoir les 30+ lignes de code ouvertes.
Hé bien, Persistence
, en plus du champ dir
a un champ options
a destination de :h sessionoptions
. Et ce petit champ bien pratique accepte l’entrée "folds"
ce qui a pour effet de mémoriser l’état des folds dans la session.
Terminé les folds à réouvrir ou refermer à chaque chargement de session, c’est tout automagique et configuré dans un bête dictionnaire lua
! Je suppose que j’aurais pu obtenir le même résultat avec Obsession
et un peu de code, mais comme dit au-dessus, là, c’est plus simple.
Configuration
Comme dit au début, j’utilise Packer
pour télécharger et configurer mes plugins:
1 use({
2 "folke/persistence.nvim",
3 as = "persistence",
4 event = "BufReadPre",
5 module = "persistence",
6 config = function()
7 require("persistence").setup({
8 options = { "buffers", "curdir", "tabpages", "folds" },
9 dir = vim.fn.expand(vim.fn.stdpath("state") .. "/sessions/"),
10 })
11 end,
12 })
Et pour ce qui est des folds, j’ai aussi ceci dans mon init.lua
:
1 vim.opt.foldmethod = "expr"
2 vim.opt.foldexpr = "nvim_treesitter#foldexpr()"
3 vim.opt.foldlevel = 99
Cela permet d’utiliser le merveilleux parsing de TreeSitter
pour savoir où créer des folds et indiquer de ne pas les replier en première lecture.
Amélioration
Là où Obsession
permettait un (n)vim -S
pour charger la précédente session, il est désormais nécessaire d’appeler lua require("persistence").load()<CR>
ou de l’automatiser.
Le premier car peut s’améliorer avec un mapping. Le second cas, s’il est lancé à chaque ouverture de nvim
va restaurer la précédente session même si nvim
a été ouvert avec un chemin vers un fichier en argument.
Ma solution est un peu naïve mais a le mérite de fonctionner:
1 if next(vim.fn.argv()) == nil then
2 require("persistence").load()
3 end
Si nvim
a été invoqué sans argument, c’est-à-dire buffer à ouvrir au lancement, alors la session précédente est restaurée. Persistence
n’émet pas d’erreur s’il n’y a pas de session à restaurer, ce qui évite un message d’erreur disgracieux.
Je sais que ce n’est pas 100% parfait. Par exemple, je pourrai charger la session si les arguments de nvim
étaient des options et pas des buffers à ouvrir. Mais je pense que ces quelques lignes couvrent largement plus de 80% de mes usages et la loi de Pareto me dit qu’il est temps de m’arrêter là.