Blog tech

L'utilisation de vim (suite)

Rédigé par Nicolas Zermati | 21 février 2012

Dans mon dernier article sur Vim j’ai introduit les bases de l’éditeur. Cette fois, j’aimerais vous présenter les fonctionnalités qui permettent à Vim de défendre sa place face à Mate ou à Sublime Text. C’est dans cet opus que vous découvrirez que la richesse des commandes de déplacement, d’insertion et de sélection n’est que la partie visible de l’iceberg.

Interactions avec la console

Lorsque vous êtes dans Vim vous pouvez exécuter des commandes classiques en les préfixant par :!. Dans ce cas le résultat de la commande est affiché puis Vim reprend la main après une confirmation de votre part. Pour avoir un terminal fonctionnel et prêt à exécuter plusieurs commandes à la suite c’est :sh qu’il faut utiliser.

On peut également rediriger la sortie d’une commande dans un buffer. En utilisant la syntaxe Ex :r !tree qui va écrire sous le curseur l’arborescence du répertoire courant. Comme toute commande Ex, on peut choisir une adresse, .,+10r !tree remplace les 10 lignes suivant la ligne courante par le résultat de tree.

Ce n’est pas terminé ! On peut aussi utiliser une partie d’un buffer comme entrée d’une commande avec :w !cmd ou encore filtrer en place tout ou partie d’un buffer. On peut par exemple supprimer toutes les lignes d’un fichier débutant par un # avec :%!egrep -v "^[\t ]*\#".

Ce qui est intéressant c’est que Vim profite de la philosophie Unix au mieux en jouant la carte du « pipe ».

Buffers de copie

Imaginez que vous venez de supprimer un texte accidentellement à coup de commande d. Vous pouvez annuler ou vous pouvez coller (la suppression de Vim agit plutôt comme un couper). Lorsque vous avez copié ou supprimé autre chose ou que vous n’avez pas envie de revenir en arrière sur les modifications que vous avez faites entre temps vous disposez d’un historique des 9 dernières copies / suppressions. La commande "2p va coller après le curseur le contenu de ce qui à été supprimé ou copié en avant dernier et ainsi de suite. Le workflow typique pour rechercher dans cet historique est "1pu.u.u. qui permet de remonter l’historique.

Lorsque vous réalisez des opérations de suppression ou de copie de texte, il est possible de contrôler la zone dans laquelle Vim stocke ce texte. C’est une fonctionnalité offerte habituellement par des gestionnaires de presse-papier. Vim offre 26 buffers de copie, un pour chaque lettre minuscule. Pour copier 5 lignes dans le buffer a on peut le faire grâce à la commande :.,+4y a ou avec "ay4. Lorsqu’on utilise des lettres majuscules, le contenu copié ou supprimé est ajouté au buffer portant la même lettre.

On peut par exemple réorganiser les sections d’un fichier en les accumulant dans un buffer (:24,31d X) dans l’ordre où l’on souhaite à la fin les voir. Lorsque c’est fait, on colle simplement le résultat avec "xp.

Macros

Je distingue deux types d’automatisation, celle que l’on prévoit, que je qualifie de statique et celle que l’on utilise au fil de l’édition, que je qualifie de dynamique.

Statiques

Abréviations

Vim dispose d’une commande permettant de définir des abréviations par exemple si je veux remplacer pck par parce que lorsque j’écris, la commande :ab[breviate] pck parce que me le permet. Cette commande n’a d’effet que dans l’instance de Vim actuelle. Vim dispose d’un fichier de configuration, dont nous reparlerons plus tard, auquel peuvent être intégrées ces abréviations et ainsi en bénéficier dans toutes les futures instances de Vim.

L’abréviation que l’on vient de définir s’applique aussi lorsque l’on essaye de taper une commande : la commande :pck 34 est étendue en :parce que 34. C’est gênant. Vim permet de choisir le mode dans lequel on veut que nos abréviations fonctionnent. Les commandes :ca[bbrev] et :ia[bbrev] permettent de définir une abréviation uniquement pour le mode commande ou insertion.

On peut bien entendu supprimer une abréviation avec les commandes :una[bbreviate], cuna[bbrev] et :iuna[bbrev] selon le type de l’abréviation à supprimer. C’est également une commande pratique pour corriger des fautes de frappe par exemple :ia tihs this.

Mapping

Vim permet de faire correspondre des touches à des actions, au-delà de la simple abréviation textuelle. Par exemple si vous trouvez que faire <Esc>:w<CR>a (voir la notation <>) n’est pas pratique pour faire une sauvegarde en cours d’édition. Alors, vous pouvez ajouter un raccourci pour le faire en appuyant sur F3 : imap <F3> <Esc>:w<CR>a. Dans cet exemple la commande :imap n’intervient qu’en mode insertion. Comme pour les abréviations, il existe un grand nombre de versions de :map. Ne pas hésiter à consulter la documentation officielle de la commande map.

Un autre exemple pourrait être de rajouter la commande Ctrl h en mode insertion pour insérer la date sous le curseur : :imap <C-h> <C-R>=strftime("%Y/%m/%d")<CR>. Dans les deux exemples j’ai utilisé des touches spéciales comme F2 ou bien Ctrl h en tant que raccourci. Il est tout à fait possible d’utiliser n’importe quelle combinaison de touches. J’oublis régulièrement de sortir du mode insertion pour faire un :w, j’ai ajouté ce mapping : :imap :w <Esc>:w.

Pour plus de détails sur la commande <C-R> voir la documentation de i Ctrl R. Cette commande permet d’évaluer des expressions diverses : opérations, fonctions, buffers, et autres puis d’écrire le résultat sous le curseur.

Fonctions

On peut déclencher des fonctions dans les macros, il devient donc possible d’écrire une fonction dans le langage Vim et de l’exécuter n’importe quand. Je ne rentrerai pas dans le détail de l’écriture des scripts Vim dans cet article mais sachez que c’est possible, simple et assez complet. On peut même utiliser du Ruby (voir la documentation de if_ruby.

Dynamiques

On peut dynamiquement utiliser des macros grâce au mode enregistrement. Ce mode permet d’enregistrer les actions que vous êtes en train de faire afin de les rappeler plus tard. Pour moi c’est vraiment une des principales killer features de Vim. Avec qa je débute l’enregistrement d’une macro dans un buffer a, lorsque j’appuie à nouveau sur q je termine cette macro. Avec @a j’exécute cette même série de commandes et d’insertions. Dès lors que l’on reformate des données complexes et répétitives, c’est indispensable.

Templates

Comme je le disais plus tôt Vim possède un fichier de configuration. Il se trouve $HOME/.vimrc et contient en général les préférences des utilisateurs. On peut y ajouter des commandes Vim classiques et également associer des actions à des évènements, par exemple l’ouverture d’un fichier. Vim ne dispose pas d’un système de modèles lors de la création de nouveaux fichiers. Grâce au système d’évènements de Vim on peut en une ligne de configuration obtenir un tel système.

Le système d’évènements de Vim va permettre de spécifier que sur l’ouverture d’un nouveau fichier on devra écrire le contenu du fichier modèle au début de ce nouveau fichier. C’est grâce à cette commande dans le fichier de configuration qu’on arrivera à ce résultat :

  autocmd! BufNewFile * silent! 0r ~/.vim/templates/tmpl.%:e

Ici BufNewFile est le nom de l’évènement, * le motif du nom des fichiers devant être retenu (tous les fichiers ici) et silent! 0r <filename> la commande qui va insérer le contenu du fichier filename à la ligne zéro du fichier. L’utilisation de %:e permet de récupérer l’extension du fichier courrant. Ainsi pour un nouveau fichier HTML on va rechercher le fichier ~/.vim/templates/tmpl.html.

Voilà, on a notre système de modèles ; à condition d’avoir préalablement enregistré les modèles nécessaires au bon endroit.

Les replis

Les replis sont une fonctionnalité qui permet de masquer une partie d’un fichier. Ces replis peuvent bien sûr être imbriqués. Dans Vim, la plus petite unité de replis est la ligne. Il existe différents modes pour contrôler ces replis : syntax, indent, manual et expr. Je n’ai pas grande expérience avec le mode de replis syntax. Le mode indent utilise uniquement l’indentation pour déterminer les niveaux de replis. Le mode manual nécessite que l’utilisateur spécifie l’intervalle a être replié. Voici un condensé des commandes pour manipuler ces replis :

Commande Action
zf Utilise le mode visuel ou un déplacement pour créer un repli
zo Ouvre le repli sous le curseur
zO Ouvre tous les replis sous le curseur
zc Ferme le repli sous le curseur
zR Ouvre tous les replis du fichier
zM Ferme tous les replis du fichier
zm Augmente le niveau de repli
zr Diminue le niveau de repli
zj Déplace le curseur au repli suivant
zk Déplace le curseur au repli précédent
[z Déplace le curseur au début du repli
]z Déplace le curseur à la fin du repli

Pour en savoir un peu plus sur les replis, je vous recommande cet article dédié aux replis de Vim. L’article aborde entre autres la persistance des replis effectués d’une utilisation sur l’autre de Vim.

Personnaliser les replis

Il y a quelques semaines, Martin posait une question relative à TextMate et au folding. Il n’y avait pas dans le bundle Markdown de replis prédéfinis concernant les sections. Voilà un petit script, qui placé dans un fichier .vim/ftplugin/markdown.vim permettra d’avoir des replis sur les sections.

  " Retourne le nombre de '#' au début de la ligne dont le numéro est en paramètre
  function! MDSectionLevel(lnr)
    let lc = getline(a:lnr)
    return matchend(lc, '^#*')
  endfunction

  " Fonction de folding pour les section markdown
  function! MDFoldLevel(lnr)
    let lvl   = MDSectionLevel(a:lnr)
    let lvlm1 = MDSectionLevel(a:lnr-1)

    if lvl > 0
      return lvl-1
    else
      if lvlm1 > 0
        return lvlm1
      else
        return '='
      endif
    endif
  endfunction


  set foldexpr=MDFoldLevel(v:lnum)
  set foldmethod=expr

Cet exemple est destiné à illustrer le mode de repli par expression. Pour utiliser ce mode, il faut donner une expression qui lors de son évaluation retourne le niveau d’indentation de la ligne numéro lnum. C’est la fonction MDFoldLevel qui est chargée de retourner ce niveau. Pour cela elle compte le nombre de # présent en début de ligne, lorsqu’il n’y en a pas, on considère que l’on est au même niveau que les lignes qui précédent en retournant '=' qui saura être interprété par Vim. Lorsqu’au contraire il y a des # en début de ligne on retourne un nombre.

Plug-ins

Avec les derniers exemples sur les templates ou le fold, on se rend bien compte que l’on ne peut pas tout écrire. Vim utilise un système de scripts pour ajouter des fonctionnalités. Des gestionnaires de paquets existent comme Vundle afin de simplifier l’utilisation de ces différents scripts. Sur la toile vous trouverez beaucoup de plug-ins facilitant l’utilisation de Ruby, Rails, Git, la recherche et l’ouverture de fichiers, les Snippets, Pastie et beaucoup d’autres choses. Sur le site officiel de Vim vous trouverez une section scripts qui répertorie les différents ajouts existants. Vous trouverez sur github beaucoup de partages de configuration Vim.

Conclusion

J’espère à travers cet article vous avoir un peu plus convaincu de l’intérêt de Vim. Et, j’espère aussi démystifier son utilisation, car beaucoup sont convaincu (à juste titre) que c’est un éditeur puissant mais très difficile à maitriser. Vim est complet, complexe, et plein de (bonnes) surprises. Pour une introduction plus complète que mes deux billets vous pouvez consulter les Vim Recipes qui sont, non pas une référence mais une introduction plus douce que la documentation officielle.

L’équipe Synbioz.

Libres d’être ensemble.