Améliorations de mes prises de notes avec Vim
Contenu
Un peu de contexte
Ma manière de prendre des notes avec Vim est minimaliste, mais fonctionnelle.
Cependant j’ai rapidement eu un souci avec vimgrep
: sa syntaxe.
En effet, pour chercher un simple motif dans les fichiers autour ou dans des sous-dossiers,
il faut faire :
:vimgrep MOTIF **
Attendre que les résultats soient trouvés, et ouvrir la fenêtre de quickfi … ah flûte ! Vim vient de m’ouvrir un buffer avec un résultat (le 1er selon la doc) … Grumph.
Bon, je reviens sur mon premier buffer,
je ferme la fenêtre quickfix (:cclose
) et, pour ne pas ouvrir automatiquement le premier résultat,
je relance une recherche ainsi :
:vimgrep /MOTIF/j **
ATTENDRE ENCORE, puis ouvrir la fenêtre de quickfix (:copen
),
naviguer sur un résultat intéressant,
et se faire ouvrir un nouveau buffer pour commencer à utiliser le fichier difficilement trouvé.
Autant dire que pour simplement rechercher un terme dans mes notes, c’était fastidieux.
Je notais donc mal, voir me servait pas de cet outil,
préférant un bon vieux
Ctrl
+
Z
, suivi d’un grep -nir PATTERN **
, puis fg
.
Fonctionnel, mais peu pratique. Il était temps que je me penche sur la question.
Amélioration de la recherche interne
Toujours dans mon optique de ne pas installer plus de plugins Vim, voici la petite solution que j’ai trouvée. Elle consiste à faire une commande spécifique pour la recherche qui va faire office de raccourci pour être lancée sans saut automatique au premier résultat, tout en ouvrant la quickfix.
Tout ceci est à faire dans le .vimrc
Tout d’abord, des commandes pour améliorer le maniement de la quickfix window :
" Show the quickfix window
nnoremap <Leader>co :copen<CR>
" Hide the quickfix window
nnoremap <Leader>cc :cclose<CR>
Ensuite, une commande pour lancer la recherche (sans saut immédiat au premier résultat) et afficher la quickfix :
command! -nargs=+ Find execute 'silent vimgrep! /<args>/j **' | copen
Et enfin, changer le comportement de la touche Entrée dans la quickfix pour ouvrir un onglet plutôt qu’un nouveau buffer :
function! QuickfixMapping()
" Ouvrir le résultat sélectionné dans un nouvel onglet,
" en fait d'abord un split horizontal, puis le changer en nouvel onglet
nnoremap <buffer> <Enter> <C-W><Enter><C-W>T
endfunction
augroup quickfix_group
autocmd!
autocmd filetype qf call QuickfixMapping()
augroup END
Satisfaction intermédiaire
Et hop ! Un comportement qui me plait bien plus avec une recherche bien plus facile à manipuler,
car je n’ai plus qu’à taper :Find MOTIF
, naviguer dans la quickfix qui s’est ouverte, et appuyer sur entrée.
Hey ! Mais ça fonctionne également ailleurs : quand je suis dans mon dossier contenant mes articles de blog, quand je suis dans un dossier de scripts … Bref, partout en fait. C’est super pratique !
Mais c’est LEEEEEEEEEEENT !
Comment est-ce que je pourrais améliorer ça ?
Ripgrep à la rescousse !
Là, il fa falloir passer par un outil externe.
Il y a bien le bon vieux grep
qui peut faire l’affaire,
mais puisque j’en suis là, autant tester ripgrep
qui à l’air extrêmement rapide.
On commence par spécifier que le grep externe à appeler est rg
s’il est présent,
tout en lui passant des arguments utiles :
if executable("rg")
set grepprg=rg\ --vimgrep\ --no-heading\ --smart-case
set grepformat=%f:%l:%c:%m
endif
Il faut ensuite changer la commande personnalisée Find
pour lui dire d’appeler grep.
On en profite pour virer les spécificités de vimgrep vues précédemment :
command! -nargs=+ Find execute 'silent grep! <args>' | copen
Wow ! Ça va déjà vachement plus vite. C’est presque instantané ! Par contre, Vim semble tout perdu. Mon affichage en dehors de la quickfix est cassé.
Je vais donc forcer Vim à refaire son rendu à la fin de la recherche :
command! -nargs=+ Find execute 'silent grep! <args>' | copen | redraw!
OK. C’est bien mieux.
Maintenant, il reste un autre souci lié au fait d’appeler un programme externe ainsi : sur la sortie standard de mon shell, le résultat de la recherche s’y trouve. Que ce soit en envoyant Vim en arrière plan, ou en le quittant tout simplement. Et il reste une espèce de clignotement. C’est dû au fait que Vim exécute les appels aux commandes externes dans son shell parent.
Rusons pour faire l’inverse en créant une fonction qui va faire l’appel à ripgrep dans un sous-shell :
function! GrepNotes(...)
return system(join([&grepprg] + [expandcmd(join(a:000, ' '))], ' '))
endfunction
Pour plus de détails et explications techniques sur l’intérêt de procéder ainsi, je vous renvoie vers le lien « Instant grep + quickfix (en anglais) » en bas de l’article.
Maintenant, modifions encore une fois la commande Find
pour appeler cette nouvelle fonction :
command! -nargs=+ -complete=file_in_path -bar Find cgetexpr GrepNotes(<f-args>) | copen
Vu que l’on part dans un sous-shell, plus besoin de forcer le rafraichissement de l’affichage de Vim. Un très bon point.
Derniers ajustements
Maintenant, un peu de cosmétique.
Je trouve que la quickfix est trop petite.
Comme on peut passer un argument (nombre en hauteur de lignes) à :copen
, voici ce que j’ai mit.
Ah, et puis au passage, je me rajoute un raccourci pour l’ouvrir verticalement.
nnoremap <Leader>co :copen 22<CR>
nnoremap <Leader>cO :vertical botright copen 90<CR>
nnoremap <Leader>cc :cclose<CR>
Et je modifie une ultime fois ma commande Find
pour l’ouverture de la quickfix :
command! -nargs=+ -complete=file_in_path -bar Find cgetexpr GrepNotes(<f-args>) | copen 22
Tout en un
Voici le contenu complet de ce que j’ai ajouté à mon .vimrc
pour en arriver là :
" QuickFix windows :
" Show the quickfix window (22 lines height)
nnoremap <Leader>co :copen 22<CR>
" Show vertical quickfix (90chr wide)
nnoremap <Leader>cO :vertical botright copen 90<CR>
" Hide the quickfix window
nnoremap <Leader>cc :cclose<CR>
" search with ripgrep
if executable("rg")
" linux :
set grepprg=rg\ --vimgrep\ --no-heading\ --smart-case
" if windows w/ git-bash (see: https://github.com/BurntSushi/ripgrep/issues/275) :
"set grepprg=rg\ --vimgrep\ --no-heading\ --smart-case\ --path-separator\ //
set grepformat=%f:%l:%c:%m
endif
" call ripgrep in a sub-shell and capture the output,
" rather than the original parent-shell call
function! Grep(...)
return system(join([&grepprg] + [expandcmd(join(a:000, ' '))], ' '))
endfunction
" search and opens quickfix windows
command! -nargs=+ -complete=file_in_path -bar Find cgetexpr Grep(<f-args>) | copen 22
function! QuickfixMapping()
" Go to the previous location and stay in the quickfix window
nnoremap <buffer> K :cprev<CR>zz<C-w>w
" Go to the next location and stay in the quickfix window
nnoremap <buffer> J :cnext<CR>zz<C-w>w
" Open selected result in a new tab (in fact, first a split, then change it
" to a new tab)
nnoremap <buffer> <Enter> <C-W><Enter><C-W>T
endfunction
augroup quickfix_group
autocmd!
autocmd filetype qf call QuickfixMapping()
augroup END
Sans oublier : pacman -Sy ripgrep
Et voilà !
Pfiou ! C’était long et fastidieux d’y arriver. Il a fallu chercher plein de choses, comprendre le comportement de Vim sur certains points (la découverte du mécanisme et du fonctionnement de la quickfix window, les appels au grep externe, etc.), mais honnêtement, ça valait le coup et c’était très intéressant.
Je suis certain qu’on peut aller plus loin,
pour éviter d’avoir à taper :Find PATTERN
en mode commande.
J’ai même vu de quoi lancer la recherche en arrière plan et avoir un affichage dynamique en fonction de ce qui est tapé via fzf
.
Mais c’est plus sage d’arrêter là pour l’instant.
Ça fonctionne très bien partout (pas seulement pour mes notes), c’est bien plus efficace et rapide qu’avant, et j’ai atteint l’objectif d’améliorer mon outillage sans utiliser de plugin. Mon système accueille un nouveau binaire, certes, mais mon éditeur et sa conf n’ont pas été trop alourdis.
C’est une bonne chose selon moi, car je peux continuer de déposer mon fichier de conf un peu partout pour garder les mêmes réflexes.
De plus, ripgrep
est disponible sur plein de systèmes.