Vim: de la survie à la création de plugins

Retour à la page Systèmes

Motivation

L’éditeur de texte Vim déroute souvent ses utilisateurs venus du monde Windows, voire d'Emacs.

Cette page montre aux novices ce que fait Vim, et le rappelle aux vétérans rouillés, sans chercher à rentrer dans les détails.

C’est surtout un condensé des raccourcis utiles à l’auteur (qui la complète encore en 2019...), allant du novice à une utilisation assez avancée: mieux vaut y faire son marché que tâcher de tout retenir.

Note: sauf exception, on écrira a pour une minuscule, A pour une majuscule. Les exceptions sont notamment C-, S-, ESC et RETURN

  • C- désigne l'appui sur la touche Ctrl, S- l'appui sur la touche Shift.

  • Pour lever l'ambiguïté sur certaines majuscules, on peut alors écrire S-c pour C majuscule par exemple.

  • ESC et RETURN désignent les touches du même nom

Certaines commandes tirent leur nom d'antiques machines comme le clavier du terminal ADM-3A utilisé par le développeur Bill Joy pour vi, l'ancêtre de Vim, et très grossièrement schématisé ci-dessous:

:     +--------------------------------------------------------------+
:     | ! | " | # | $ | % | & | ' | ( | ) |   | * | = | { | } | Home |
:     | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | : | - | [ | ] | ^ ~  |
:  +-----------------------------------------------------------------+
:  | Esc | Q | W | E | R | T | Y | U | I | O | P | LF | Enter | Here |
:  |     |   |   |   |   |   |   |   |   |   |   |    |       |      |
:  +-----------------------------------------------------------------+---+
:    | Ctrl| A | S | D | F | G | H | J | K | L | + | ` | | | Rub | Break |
:    |     |   |   |   |   |   | <-| v | ^ | ->| ; | @ | \ |     |       |
:    +-------------------------------------------------------------------+
:    | Ctrl | Z | X | C | V | B | N | M | < | > | ? | Shift | Rpt| Clear |
:    |      |   |   |   |   |   |   |   | , | . | / |       |    |       |
:    +-------------------------------------------------------------------+

Survie

Les "Modes" de vim

L’effet d’une touche dépend du mode courant. Les modes principaux sont assez nombreux (cf :h map-modes). En pratique on a souvent une des situations suivantes:

  • Mode normal: celui d’où on lance les autres commandes:'i' pour insertion ':' pour mode ed, 'q' pour macro, V ou v pour sélectionner

  • Mode insertion: celui où on tape le texte: ESC revient en mode normal, C-v entre un caractère spécial.

  • Enregistrement de macro: 'q' termine l'enregistrement et revient au mode normal

  • Ligne de commande (alias ed): ESC revient en mode normal ':!' exécute une commande du shell

  • Sélection : ESC revient au mode normal

Pour les débutants absolus de Vim:

  • :h : version courte de :help, à lire avant toute chose

  • :h tutor : pour (re)découvrir les bases

  • Dans le manuel, C-o C-i vont à l'emplacement précédent/suivant, et C-] va au mot-clef situé sous le curseur;

Autant que possible, ce qui suit mentionnera:

  • la commande :h {mot} menant à la partie correspondante du manuel (extrêmement riche).

  • une interprétation mnémotechnique du nom de la commande présentée. Souvent la première lettre donne un thème, et les autres y apportent une précision

Manipulations élémentaires des fichiers

  • :e {fichier} : (Edit) ouvre un fichier dans un nouveau buffer.

  • :bd : (Buffer Delete) Ferme le buffer courant.

  • :w : (Write) Enregistre le buffer courant

  • :q : (Quit) Ferme Vim ou la fenêtre courante.

  • :q! : Ferme la fenêtre courante ou Vim sans rien enregistrer

  • :x : (eXit) Enregistre les changements et ferme la fenêtre.

  • :v : (View) Ouvre un fichier en lecture seule.

Passer en mode insertion de texte:

  • i I : à gauche du caractère courant/en début de ligne

  • a A : à droite du caractère courant/en fin le ligne

  • r R : écraser un caractère/se mettre en mode 'écrasement'

  • o O : créer une nouvelle ligne en-dessous/au-dessus

Saisir du texte

  • En général le caractère voulu est sur le clavier;

  • En mode normal, ga (Get Ascii) donne le code hexa du caractère courant;

  • En mode commande, on peut substituer un caractère spécial: :%s/\%xHH/c/gc pour changer (H:chiffre hexadécimal, mettre xHH pour deux chiffres, uHHHH pour quatre, UHHHHHHHH pour huit);

  • Le mode insertion a de multiples façons de saisir un caractère spécial (voir cet article) et :h c_digraph :

    • Composition: C-k c , donne ç;

    • Saisie hexa: C-v u 00a9 donne ©; plus généralement C-vuxxxx ou C-vUxxxxxxxx insère un caractère unicode x...x (voir sur wikia.com)

    • Echappement: C-v {ENTREE} donne ^M (symptôme: le curseur ne va que sur le ^, et le group ^M est en couleur)

Fenêtres et onglets

Fenêtres

  • C-w : (Window) commande sur les fenêtres, équivaut à :wincmd

  • C-w j, C-w k, C-w h, C-w l : fenêtre du dessous/dessus/gauche/droit (hérité des flèches des anciens terminaux);

  • C-w s, C-w v : divise la fenêtre horizontalement (Split) ou verticalement (Vertical);

  • :set lines={n} columns={m} : taille de la fenêtre;

  • C-w +, C-w - : Agrandir/réduire la fenêtre courante. 5 C-w + gagne 5 lignes.

  • :close : Ferme la fenêtre courante (mais pas son buffer);

  • :only : Etend la fenêtre courante et ferme les autres;

Les résultats de certaines fonctions avancées (compilation, fonctions des plugins) se trouvent dans des fenêtres d'interface spéciales:

  • Quickfix : partagée entre toutes les autres fenêtres :copen, :cc, :cnext, :cprevious, :cclose, surtout pour naviguer entre les messages d'erreur de compilation;

  • Liste des emplacements : propre à une fenêtre :lopen, :ll, :lnext, :lprevious, :lclose, surtout pour lister les emplacements trouvés par une recherche;

Onglets

  • :tabnew :tabnext :tabprev :tabclose : création/navigation de buffers dans des onglets.

  • gt gT : (Go to Tab) tab précédent/suivant

Folds (pliages)

Les commandes de "pliage" (to fold en anglais) cachent/montrent des sections de texte. Elles commencent par z (dont la forme évoque un pliage) et sont décrites par :h fold-commands

  • zf{move}, zc, zo : (Fold) crée un pli à la ligne du curseur, le contracter, l’ouvrir

  • zC, zO : (Contract) ferme les plis à la ligne du curseur, (Open) les ouvre tous.

  • zr, zR : Ouvrie partout un niveau de pli, tous les niveaux;

  • zm, zM : (Minimize) ferme partout un niveau de pli, tous les niveaux;

  • zd, zD, zE : (Delete) supprime un pli existant sur un niveau, sur tous les niveaux, (Erase) sur tout le fichier.

Déplacements et marques

Fichiers et répertoires

  • :pwd : (Print Working Directory) Affiche le nom du répertoire courant.

  • :cd {rep} : (Change Directory) change le répertoire courant.

  • :ls : (LiSte) liste les buffers (i.e. les fichiers ouverts)

  • :b{n} : (Buffer) va au buffer {n}

  • :saveas {nom} : enregistre le buffer courant sous un autre nom

  • :bd {n} : (Buffer Delete) ferme le buffer {n}

  • u, C-r : (Undo/redo): annule/rejoue les dernières modifications.

  • g-, g+ : (Undo/redo): idem, mais par ordre chronologique.

Se déplacer dans le texte.

Ces commandes sont souvent combinées avec celles de copier/coller par exemple:

  • h j k l : bouge d'un caractère à gauche, en bas, en haut, à droite (héritage du terminal ADM-3A).

  • b e : (Beginning/End) Début/fin du mot courant

  • 0 $ : début/fin de ligne

  • ) } ]] ( { [[ : phrase/paragraphe/section d'après/avant.

  • f {l}, F {l} : (Find) va sur la lettre {l} d'après/avant.

  • t {l}, T {l} : (T) va juste avant la lettre {l} d'après/avant.

  • ; , répète la dernière opération f ou F dans le même sens ou non.

  • m {m} : (Mark) met une marque a-z (par fichier) A-Z (globale) 0-9 (voir help)

  • '{m} `{m} : va à la ligne/au caractère de la marque {m}

  • < > . ' " ^ : marques de debut (<)/fin(>) de sélection, dernier changement(.), dernière sortie de buffer ("), dernière insertion (^).

  • {n}G, {n}go : (Go) aller à la ligne ou à l’octet {n}

Choisir la zone où opérer

Souvent on veut n'opérer que sur certaines portions de textes. Voici des exemples appliqués à la commande :s:

  • v : (Visual) Sélection caractère par caractère

  • V : Sélection ligne par ligne

  • C-v : Sélection rectangulaire

  • :'<,'>s/avant/apres/gc : Entre début et fin de sélection courante

  • :%s/avant/apres/gc : Sur tout (%) le fichier, substituer (s) avant par après, globalement (g) sur chaque ligne et en demandant confirmation (c).

  • :13,25s/avant/apres : Entre les lignes 13 et 25 incluses

  • {ESC} : n'affiche plus la sélection.

  • gv : affiche à nouveau la dernière sélection

En mode normal, la plupart des commandes attendant {move} acceptent aussi des objets textes (cf :h text-objects) à la place:

  • iw, iW : mot ou MOT courant

  • aw, aW : idem avec l'espace qui suit

  • is, as : phrase (sans/avec espace)

  • ip, ap : paragraphe (sans/avec espace)

Pouvoir préciser ainsi la portée d’une commande sans sortir du mode normal est très confortable. Par exemple, viw sélectionne le mot courant, cip écrase le paragraphe courant, etc.

Mieux encore, les objets textes peuvent aussi couvrir une zone entre deux délimiteurs:

  • ib, ab : inside / around parenthèse, ou i), a);

  • iB, aB : inside / around accolade (Brace), ou i}, a};

  • it, aT : inside / around tag XML;

  • i", a" : inside / around quote;

  • i', a' : inside / around quote simple;

Recherche et remplacement

Rechercher du texte

Combinées avec les opérations sur une zone limitée, les commandes de recherche sont puissantes.

  • /{motif} : recherche

  • /\c{motif} : recherche (indifférente à la casse)

  • /\C{motif} : recherche (sensible à la casse)

  • :%s/{avant}/{après}/gc; : remplacer (et confirmer) toutes les occurrences de {avant} par {après}.

  • :nohlsearch : Ne plus surligner les résultats de recherche.

  • & : répète la dernière substitution sur la ligne courante.

  • *, # : chercher le mot courant seul, en avant/arrière

Certaines commandes (souvent préfixées par g) concernent une navigation itérative:

  • g*, g# : cherche le mot courant (éventuellement inclus dans un autre mot) en avant, arrière;

  • g,, g; : va à la modification suivante, précédente;

  • C-i, C-o : va au saut suivant, précédent;

  • gf : ouvre le fichier dont le nom est sous le curseur;

  • g'm, g`m : rejoint la marque m , sans changer la jumplist;

Edition de code

Sans atteindre la puissance d'un IDE, Vim dispose de raccourcis commodes pour la programmation, notamment en C et C++:

Passer d'un bloc à l'autre

Ces commandes sont composées de [ ou ] pour indiquer précédent/suivant, et du délimiteur recherché;

  • [( [{ ]} ]) : parenthèses/accolades de début/fin du bloc courant.

  • [m [M ]m ]M : précédent([)/prochain(]) début(m) ou fin (M) de fonction.

  • [# [/ ]# ]M : idem pour directives de préprocesseur et commentaires C.

Aide en ligne

  • K: appelle un programme d'aide sur le mot courant (par défaut man)

  • :h {mot}: consulte le manuel Vim (ex: :h :help)

Registres et copier-coller

Copier/Coller du texte:

Les commandes de copier/couper/coller peuvent toutes être précédées d'un nom de registre à utiliser, via la séquence "xx est à remplacer par le nom du registre à utiliser (défaut ").

Pour copier

  • y{d} : (Yank) copie du point courant au déplacement {d} qui suit. Par exemple ye copie jusqu'à la fin du mot courant.

  • "{R}y : Ajoute la sélection au registre {r} (c'est la majuscule qui dit qu'il faut ajouter au lieu d'écraser)

Pour couper (et passer en mode insertion):

  • c{move} : (Cut) coupe (et stocke) le texte couvert par {move}

  • {count}s : (Substitute) coupe (et stocke) {count} caractères.

  • {count}C : (Cut) coupe (et stocke) {count} lignes.

  • {count}S : (Substitute) idem, avec lignes entières.

Pour couper (et rester en mode normal)

  • {count}x : (eXtract?) coupe (et stocke) {count} caractères.

  • {count}X : (eXtract?) idem, mais en arrière.

  • {count}D : (Delete) coupe (et stocke) {count} lignes à partir du caractère courant.

  • {count}dd : (Delete) idem, avec lignes entières.

Pour coller

  • P : (Paste) insère à gauche du curseur ( "{r}P pour un autre registre {r}).

  • p: (Paste) insère à droite du curseur

  • J : (Join) Joint deux lignes (va au bout de la ligne courante et supprime le saut).

Compléments sur les registres:

  • :registers : affiche le contenu de tous les registres

  • :help registers : à consulter pour les détails

  • C-r {r} : en mode insertion, colle le contenu du registre {r}

  • C-rC-r {r} : idem mais sans rien interpréter (utile avec des caractères de contrôle)

  • Registres spéciaux: %(buffer courant) /(dernière recherche) +(presse-papier vim) *(presse-papier global) 0 (dernière copie) 1-9 (dernières suppressions) "(défaut) _(NULL) :(dernière commande) .(dernière insertion) ...

  • :let @{r} = "..." : Décider du texte d'un registre

  • C-r = {calcul} : en mode insertion, insère le résultat de {calcul}. Par exemple C-r = 3*5 RETURN insère 15

Personnaliser Vim

Macros

La touche @ interprète un registre comme une suite d'appuis de touches (une 'macro').

  • Créer: q{m}{commandes}q stocke {commandes} dans le registre {m} (attention: c’est bien le même que pour le copier-coller)

  • Rejouer: @{m} ou @@ (rejoue la dermière macro)

  • Ajouter: q{M}{suite}q : la majuscule {M} fait ajouter {suite} au registre {m}.

  • :{range} normal @m : Exécute la macro sur certaines lignes

  • Macro avec calculs: 100-(nombre courant): :let @i="dei^R=100-^R\"^M^["

En plus d'enregistrer les macros, la touche q permet des manips utiles:

  • q: : édition de commandes (Entrée pour exécuter la ligne courante)

  • q/ : édite une chaîne à rechercher (Entrée pour débuter la recherche)

  • Joker: C-f : pour retrouver l'effet de q: ou q/ après avoir juste tapé : ou /

Le fichier .vimrc peut définir des macros dès le démarrage.

  • Le plus souvent : let @r='chose', où chose peut être complété par les insertions littérales de caractères comme C-v{touche}, ou de registres comme C-rC-r{reg};

  • Piège: let ajoute ^J si la macro finit par ^M (touche RETURN). En cause, le mode ligne de 'let-@'. Solution: remplacer let ... par call setreg("r",'chose','c'), qui interpréte chose par caractère;

Clavier

Associer une touche à une action est plus sûr et plus durable qu’une macro, dont le registre risque sans cesse l'écrasement

Voici des raccourcis extraits du fichier .vimrc de l'auteur:

"Pour l'édition de texte en général
"* Redessiner l'écran sans surligner la recherche courante
nnoremap <C-l> :redraw<CR>:nohlsearch<CR>
"* Supprimer les espaces de fin de ligne
nnoremap <F5>s :%s/[ <TAB>][ <TAB>]*$//gc<CR>

"Pour compiler et chercher les erreurs
nnoremap <F7> :make -k<CR>
nnoremap <F4> :cnext<CR>
nnoremap <S-F4> :cprev<CR>

Particularités des fonctions de la famille ...map:

  • On pourrait écrire :nnoremap ... : le «:» est facultatif dans le fichier .vimrc, mais utile pour exécuter la commande sans recharger le fichier.

  • La notation en <> évite d'entrer les caractères spéciaux littéralement: <CR> représente la touche Entrée, <C-x> la combinaison Ctrl-x, etc.

  • Voir :help map-modes pour choisir entre map, nmap, ..., vmap, etc. selon le mode où l’on veut appliquer le raccourci.

  • Préférer les versions en ...noremap, qui évitent les embrouilles en n’interprétant pas récursivement les raccourcis.

  • :map donne la liste des raccourcis déjà définis;

  • :h map-multibyte avertit des pièges des raccourcis à base de caractères non-ascii;

  • La notation <Leader> désigne une touche personnalisable (voir :h Leader) pour commencer vos raccourcis;

Plugins

L’utilité des plugins pour étendre les fonctions de base de Vim apparaît notamment sur de gros projets de code.

  • Idées pour rapprocher le comportement de Vim de celui d’un IDE: voir Wikia.com.

  • Mon installation "perso" utilise:

    • Fugitive : pour contrôle de version de code avec Git

    • Surround : change ou ajoute simplement un délimiteur autour d'une zone sélectionnée par les mouvements habituels.

    • Vim-rtags : extrêmement puissant pour des projets compilés en C/C++ (demande clang, et la compilation de l'utilitaire rtags)

Options

Connaître les options de Vim évite de réinventer la roue à coups de plugins et de macros.

Manipulations

  • :options : liste les options, leur rôle et leur valeur actuelle.

  • :set : liste les valeurs actuelles des options, sans expliquer.

  • :set {option}? : Affiche la valeur courante

  • :set {option}! : Change la valeur d'une option booléenne

  • :set {option} : Active une option booléenne

  • :set {nooption} : Désactive une option booléenne

  • :set {option}={valeur} : change la valeur globale de l'option non-booléenne.

  • :setlocal {option}={valeur} : change la valeur locale de l'option pour ce buffer.

  • :setlocal {option}< : met l'option à sa valeur globale.

  • :setglobal {option}=... : met une valeur globale

  • :set {option}< : enlève la valeur locale et n'utilise que la valeur globale.

Options quotidiennes

  • :set number / nonumber / number! : numéroter les lignes

  • :set syntax={type} type pour la coloration syntaxique (:syntax montre le fichier de syntaxe actuel)

  • :set nohlsearch : ne pas/plus surligner les éléments cherchés par /.

  • :set fileencoding=... : encodage (utf-8,latin1,...) du buffer dans son fichier

  • :set encoding=... : encodage du buffer pour le terminal

  • :set fileformat=dos,unix,mac : format des fins de ligne

  • :set autoread : pour relire automatiquement un fichier changé ailleurs.

  • :set noet : noexpandtab (ne pas convertir tabulations en espaces et faire suivre par :retab)

  • :set tw=0 : ne pas couper les lignes de texte à l'insertion (par buffer).

Autocompletion

L’auto-complétion a énormément d’options (sans compter les plugins) dont voici des exemples pris dans Vim 8.

En mode commande, elle est décrite par :h cmdline-completion

  • :set wildmenu: affiche une liste des choix par TAB, C-n, C-p, etc.

  • :set wildmode=list:longest,full: montre le plus long préfixe certain

En mode insertion, loire les commandes avec :h ins-completion:

  • C-n et C-p affichent des choix puis naviguent entre eux.

  • C-nC-p raffine le filtrage en continu (pour valider, prendre le mot par un dernier `C-n` puis taper un espace).

Scripts, commandes et fonctions

Le fichier .vimrc stocke un peu de tout: macros et touches, certes, mais aussi vos définitions de commandes, fonctions et plugins;

Ainsi, doc du langage vimscript (:h vim-script-intro`, :h write-plugin), m’a guidé pour la mise en bouche suivante (définir une commande perso :Th qui écrit l’heure et passe en mode insertion):

" Insère l’heure, sur une nouvelle ligne
function Do_Th ()
  "read! insère la sortie du shell dans une nouvelle ligne
  execute "read!date +\"\\%H:\\%M\""
  "passe en mode insertion, ce que 'normal' ne fait pas
  call feedkeys('A: ')
endfunction
command! Th call Do_Th()

Même si ce n’est pas parfait, voici les raisons des choix d’écriture:

  • command! délègue le travail à une fonction pour la facilité de lecture;

  • execute "read!..." fait comme si on tapait :read!... qui invoque une commande du shell;

  • feedkeys(...) simule des appuis de touches et semble le seul moyen de passer du mode normal à l’insertion;

Autre besoin un tout petit peu plus évéolué: une commande :Search qui cherche son argument dans le fichier courant, et montre la liste des résultats dans la fenêtre QuickFix:

"Rechercher toutes les occurrences d’un mot et les lister dans QuickFix
function Do_Search (motif)
  execute "vimgrep /".a:motif."/ ".bufname("%")
  copen
endfunction
command! -nargs=1 Search :call Do_Search(<q-args>)
nnoremap <Leader>/ :Search<SPACE>

Cet apéritif illustre quelques caractéristiques du langage:

  • set option = valeur : affecter ou modifier une option;

  • let variable = .... : affecter ou modifier une variable;

  • le nommage des variables y est important:

    • s:nom, b:nom : variables locales à un script ou à un buffer.

    • noms spéciaux : $nom (environnement), &nom (option), @nom (registre)

  • execute ... : exécution de commandes dynamiques;

  • eval(...) : évaluation (sans exécution);

  • function(x, ...) : définit une fonction, avec a:{index} pour les arguments variables;

  • supporte les listes et les dictionnaires;

  • comporte une structure try, catch, finally...;

Consultez l’Internet pour échapper aux nombreux pièges qui guettent le débutant enthousiate:

  • Confusion entre références des options par &, et des variables par leur nom;

  • Confusion des affectations d’options par set, et de variables par let

  • set n'évalue rien, mais let peut concaténer, référencer des variables, etc.

Pour aller plus loin

Sur les plugins:

Edition en hexadécimal:

  • Avec le programme xxd (pas si facile)

Avancé: