NVim et Telescope: dernières découvertes
Le weekend dernier avait lieu les Journées du Logiciel Libre et à leurs programme pléthore de sujets passionants. Parmis les sujets qui me touchent le plus, on y a parlé de claviers ergonomiques sur un stand dédié et durant une conférence, mais aussi de neovim
via conférences et #TupperVim.
L’avantage d’être entouré de centaines de personnes partageant notre enthousiasme pour ces sujets et qu’on y apprend forcément quelque chose. Petit billet donc pour deux découvertes me concernant au tour de nvim
.
J’en ai déjà parlé à de multiple reprises sur ce site: j’utilise nvim
. C’est pour moi un outil central pour toute rédaction, qu’il s’agisse de code, de documentation ou de simple prise de note.
L’un des plugins incontournable dans l’écosystème de nvim
est telescope
. Telescope
propose une interface unifiée de fuzzy-finding : grep
, git files
, buffers
et j’en passe.
Si j’ai tenté d’autres approches comme harpoon
pendant un temps, je suis toujours revenu à telescope
pour me balader entre mes buffers ouverts ou accéder à ceux qui ne le sont pas encore. C’est tellement devenu un automatisme que j’ai désormais les mappings suivants pour ouvrir telescope
facilement:
local builtin = require("telescope.builtin")
vim.keymap.set("n", "<Leader><Leader>", builtin.buffers, { desc = "Telescope buffers" })
vim.keymap.set("n", "<Leader>|", builtin.git_files, { desc = "Telescope git files" })
vim.keymap.set("n", "<Leader>tg", builtin.live_grep, { desc = "Telescope live grep" })
vim.keymap.set("n", "<Leader>th", builtin.help_tags, { desc = "Telescope help tags" })
vim.keymap.set("n", "<Leader>tk", builtin.keymaps, { desc = "Telescope keymaps" })
Navigation entre les buffers
Comme montré ci-dessus, j’utilise <Leader><Leader>
pour faire apparaître le buffer picker. Il suffit ensuite de taper quelques lettres contenues dans le chemin d’un fichier pour raffiner la sélection et la valider avec <CR>
.
Mais j’utilise aussi <Leader>|
et <leader>tg
pour naviguer entre les fichiers versionnés ou rechercher une chaîne de caractères dans l’ensemble des fichiers accessibles. Ce sont mes deux principales méthodes pour charger de nouveaux fichiers dans des buffers.
La liste des buffers peut vite s’agrandir, notamment lors d’une session exploratoire quand il s’agit de traquer un bout de code (généralement appelé bug par les clients et fonctionnalité par les commerciaux 🤷). De fait, le *buffer picker présente de plus en plus de lignes qui ne seront en fait plus utilisées et un peu de ménage devient nécessaire.
Avant (c’était pas mieux)
Auparavant, j’utilisais une fonctionnalité native de (n)vim
: bdelete
.
Son usage est simple:
:bdelete<CR>
ferme le buffer courant,:bdelete buffer<CR>
ferme le bufferbuffer
,:bdelete regex<C-a><Cr>
ferme tous les buffers dont les noms matchent laregex
.
Problème, les deux premières méthodes sont précises mais lentes alors que la dernière, rapide, peut fermer trop de buffers si la regex
est trop générale.
Maintenant (c’est le futur donc c’est mieux)
L’un des participants au tupper vim des JdLL nous a présenté le plugin buffer manager
. Ce dernier propose (entre autres) une interface interactive pour la sélection et la fermeture des buffers.
Après intense réflexion, je me suis dit qu’il devait forcément y avoir un moyen d’obtenir le même résultat avec telescope
. Après tout, s’il peut ouvrir et lister des buffers, il doit bien pouvoir les fermer. Et la réponse est: oui.
1 require("telescope").setup({
2 pickers = {
3 buffers = {
4 show_all_buffers = true,
5 sort_lastused = true,
6 mappings = {
7 i = {
8 ["<c-d>"] = "delete_buffer",
9 }
10 }
11 }
12 }
13 })
Ce petit bout de configuration ajoute le mapping <C-d>
dans l’interface de telescope
pour fermer le buffer sous la sélection courante.
Donc le workflow devient trivial:
<Leader><Leader>
pour ouvrir le buffer picker detelescope
,- Fuzzy finding pour réduire la sélection possible interactivement,
<C-d>
jusqu’à ce que ce ne soit plus nécessaire.
Hop, pas besoin de plugin supplémentaire pour cette fonctionnalité bien pratique.
Rechercher/Remplacer global
Tous les utilisateurs ayant dépassé le vimtutor
connaissent la commande :[range]s[ubstitute]/{pattern}/{string}/[flags] [count]
. Utilisable en version courte :s/search/replace
, c’est toute la puissance de sed
à portée de l’éditeur.
Sauf que voilà, il est parfois nécessaire de modifier plus loin que le buffer courant pour garder une cohérence à l’échelle d’un projet entier. Alors, oui, nous sommes en 2023 et il y a les LSP1 qui font des miracles pour renommer variables et fonctions. Mais ils ne sont pas toujours présents ou n’ont pas toujours toutes les fonctionnalités requises et il faut de temps en temps en revenir à des méthodes datant de vim
(ou est-ce de vi
?).
Avant (nan, vraiment, c’était pas mieux)
Ma méthode favorite avant les JdLL était efficace mais peu sure et obscure.
:args regex<CR>
:argdo %s/search/replace/ge | update
La première ligne avec args
permet de définir une liste de buffers. Par exemple, pour choisir tous les fichiers Rust
🦀 d’un dépôt: :args src/**/*.rs<CR>
est efficace.
La seconde ligne combine deux commandes: argdo
va appeler la substitution sur les buffers chargés dans la liste par args
avec l’option e
pour ne pas s’arrêter si search
est absente. Puis update
afin d’écrire le contenu des buffers sur disque si et seulement s’ils ont été modifiés (une sorte de :w[rite]
intelligent).
Maintenant (pas avant, donc mieux)
J’ai abordé <Leader>tg
(pour telescope grep
) tout à l’heure: un grep
avec fuzzy finding intégré à l’éditeur. Je m’en étais toujours servi pour charger des fichiers dans des buffers. Mais il y a une autre utilité à ce picker dans le contexte présent 🎉
<Leader>tg fuzzy-finding<C-q>
. Le <C-q>
va venir charger les résultats de la recherche dans la quick-fix list de nvim
. Cela permet une étude détaillée des résultats de la recherche contrairement à la méthode précédente qui peut être utilisée avec une regex
trop large avec argdo
. Il devient donc possible de répéter le processus jusqu’à l’obtention contrôlée de l’ensemble minimal recherché.
La suite, c’est comme argdo
mais avec cfdo
pour appliquer une commande aux buffers répertoriés dans la quick-fix list. La méthode est plus sûre, car il est aisé de raffiner itérativement sa recherche mais aussi moins obscure: si quelqu’un ne connaissant pas nvim
me voyait utiliser les deux méthodes, il est certain qu’il comprendrait bien mieux la seconde approche qui est donc à privilégier en cas de pair-programming.
Conclusion
Comme disait l’autre: Sharpen the saw.
Ces deux tips ne font pas de moi un meilleur programmeur, mais ils me permettent un usage plus sain et évident de mon outil de travail principal: l’éditeur de texte.
J’espère apprendre au plus vite de nouvelles approches pour mieux répondre à mes besoins dans ce domaine!
Language Server Protocol