arthur.bebou

Le site arthur.bebou.netlib.re - retour accueil

Anonyme, en lecture seule : git clone git://bebou.netlib.re/arthur.bebou
Authentifié·e, en écriture : git clone ssh://git@bebou.netlib.re:1459/srv/git/arthur.bebou

Log | Files | Refs | zip | tar.gz |

index.sh (24781B)


      1 #! page
      2 title: Améliorer la découvrabilité des CLI : l\'auto-complétion
      3 author: Arthur Pons
      4 description: Les CLI sont peu découvrables. Voyons comment écrire de l\'auto-complétion custom pour améliorer tout ça avec l\'exemple de tricount
      5 publication: 2026-05-29
      6 
      7 section: main
      8 
      9 ## Le problème
     10 
     11 Très rapidement : les outils en ligne de commande sont peu découvrables. On
     12 tape une commande puis quoi ?
     13 
     14 ![Un terminal, texte blanc sur fond noir. On y voit la commande mv tapée, le
     15 curseur juste derrière avec rien d'autre d'écrit](rien.jpg)
     16 
     17 On lit le manuel GNU de ses morts ? On tente peut-être un `-h` ou un `--help`
     18 qui listera un sous-ensemble des options sans trop d'explications, ou au
     19 contraire, imprimera la bible toute entière[^1] ?  Si l'outil est pas trop
     20 compliqué, mettons `mv`, c'est jouable. Et encore, il faut savoir que `man mv`
     21 existe, désepérer du fait que ce soit un manuel de *référence*, éventuellement
     22 savoir que `info mv` existe, probablement plutôt aller voir des tutos sur
     23 internet et se rendre compte que, puisque l'on est en 2026, la plupart sont
     24 générés par IA. Bref pas facile.
     25 
     26 Une solution pourrait être de faire de l'auto-complétion. Grâce à la magie de ce
     27 qu'il se passe lorsqu'on appuie sur la touche tabulation en écrivant une
     28 commande dans un shell moderne[^2] le shell peut nous indiquer ce qu'il est
     29 possible d'écrire à cet endroit de la commande.
     30 
     31 J'ai trouvé très peu de tutoriels satisfaisant sur la question donc j'en écris
     32 un :)
     33 
     34 ## Les limites
     35 
     36 Dans cet article je partage ce que j'ai appris en écrivant l'auto-complétion
     37 `zsh` pour l'outil [`tricount`](http://git.bebou.netlib.re/tricount). Il en
     38 découle :
     39 
     40   * qu'il ne couvre pas l'auto-complétion `bash` (mais j'ai cru comprendre que
     41     c'était similaire)
     42   * que l'on ne parle pas de découvrabilité lorsque l'on ne sait même pas quelle
     43     commande taper (voir plutôt [zenu](http://arthur.bebou.netlib.re/zenu) pour
     44     ça)
     45   * que ce n'est pas un tutoriel complet sur l'auto-complétion `zsh` mais
     46     uniquement ce dont j'ai eu besoin pour `tricount`.
     47 
     48 Par ailleurs j'émets l'hypothèse que l'auto-complétion est significativement
     49 utile pour la découvrabilité d'une commande mais je ne l'ai pas réellement
     50 testé. Pour pouvoir réellement mesurer l'intérêt de l'auto-complétion pour la
     51 découvrabilité il faudrait que je le fasse tester à des copaines sans et avec
     52 l'auto-complétion mais on est pas ici pour faire de la recherche lol 🤓
     53 
     54 ## Le résultat
     55 
     56 L'objectif est de passer de ça :
     57 
     58 <video src="sans-auto.mp4" controls loop muted loading=lazy>
     59 
     60 à ça (vidéo de 500Ko, cliquez dessus pour lancer) :
     61 
     62 <video src="avec-auto.mp4" preload=none controls muted loading=lazy>
     63 
     64 On remarquera que l'outil fonctionne avec une syntaxe de *commande*,
     65 *sous-commande* et *objet*. Les *sous-commandes* et *objets* disponibles
     66 dépendent de la *commande* choisie.  Par exemple la commande *creer* ne
     67 nécessite rien d'autre mais si l'on choisit la commande *ajouter* il
     68 faudra ensuite choisir un *objet* entre *dépense* et *personne*. Si l'objet est
     69 *dépense* il faudra choisir le nom d'une personne puis un montant etc.
     70 
     71 Ce qui rend l'auto-complétion vraiment utile est qu'elle est entièrement
     72 *contextuelle*. C'est ce que fait par exemple l'auto-complétion `zsh` de la
     73 commande `sed`. L'auto-complétion sait si l'on est en position d'écrire une
     74 nouvelle commande (ici `s///`) ou d'apporter une modification à une commande `s`
     75 (`g` et `i` par exemple) :
     76 
     77 <video src="sed-auto.mp4" controls loop loading=lazy>
     78 
     79 ## Le code
     80 
     81 ### La base, `compadd`
     82 
     83 L'auto-complétion de `zsh` repose sur des fonctions d'auto-complétion[^9]. Ces
     84 fonctions sont des fonctions shell classiques appelées à chaque fois que
     85 l'utilisateurice appuie sur la touche tabulation[^4]. Elles commencent
     86 habituellement par un `_` et il est apparemment conventionnel de les nommer
     87 du nom de la commande qu'elles complètent bien que ce ne soit pas obligatoire.
     88 Imaginons donc une fonction de complétion tout à fait inutile affichant `a`,
     89 destinée à compléter la commande `a`[^5] :
     90 
     91 ```
     92 _a(){ printf "a"; }
     93 ```
     94 
     95 Pour l'associer à la commande `a` (inexistante mais c'est accessoire pour le
     96 moment) il existe une commande `compdef` qui prend en argument le nom d'une
     97 fonction puis le nom de la commande qu'elle doit compléter :
     98 
     99 ```
    100 compdef _a a
    101 ```
    102 
    103 Pour vérifier le résultat il suffit d'initier une commande `a`, de mettre un
    104 espace puis d'appuyer sur tabulation :
    105 
    106 <video src="a-auto.mp4" controls loop loading=lazy>
    107 
    108 C'est très bien mais ça ne permet pas l'auto-complétion. Pour programmer ce que
    109 l'on souhaite, c'est à dire la sélection des options et arguments parmi un
    110 ensemble restreint il faut utiliser une seconde fonction `zsh`, `compadd`[^6].
    111 
    112 Dans sa forme la plus simple `compadd` s'utilise en lui passant en argument
    113 les "candidats" à l'auto-complétion. Si l'on ajoute `1234`, `5678` et `91011`
    114 alors ces trois chaînes seront proposées à l'auto-complétion via un petit menu
    115 s'affichant en dessous de la ligne de commande en cours et navigable avec les
    116 flèches. On verra [plus
    117 tard](/auto-completion/#dcorrler-laffichage-des-candidats-de-largument-auto-complt-avec--ld)
    118 qu'il est possible de modifier l'affichage de ce menu pour par exemple donner
    119 des informations à propos des candidats.
    120 
    121 Si l'on commence à écrire un argument et que le début match avec l'un de ces
    122 candidats de manière non ambiguë, l'auto-complétion l'ajoutera automatiquement :
    123 
    124 <video src="compadd-auto.mp4" controls loop loading=lazy>
    125 
    126 Puisque la fonction dans laquelle on l'écrit est une fonction shell
    127 classique on peut entourer les `compadd` de toute la logique nécessaire pour
    128 trouver les bons candidats et les afficher au bon moment[^10]. Reste alors à
    129 connaître les différentes options pratiques de `compadd` et les variables
    130 disponibles pour connaitre l'état de la commande en cours d'écriture.
    131 
    132 ### Ses options
    133 
    134 #### Afficher des message d'information ou d'erreurs
    135 
    136 Pour ajouter un texte explicatif avant les candidats on peut utiliser l'option
    137 `-X` ou `-x` de `compadd`. `-X` s'affichera uniquement s'il existe des candidats,
    138 `-x` s'affichera quoi qu'il arrive. `-x` est donc assez utile pour afficher des
    139 messages "d'erreurs". Ainsi dans la vidéo suivante on utilise `-X` pour
    140 afficher le message "Choisir une personne à retirer" mais `-x` pour afficher
    141 "Personne à retirer" :
    142 
    143 <video src="compadd2-auto.mp4" controls loop loading=lazy>
    144 
    145 #### Auto-compléter plusieurs candidats d'un coup avec `-Q`
    146 
    147 Il peut être utile qu'un unique candidat soit lui même la concaténation de
    148 plusieurs arguments. Disons par exemple qu'à un état de l'auto-complétion on
    149 veuille faire exécuter la sous-commande "cmd1" suivi de la sous-sous-commande
    150 "cmd2" à l'utilisateurice. Si l'on ajoute naïvement la chaîne "cmd1 cmd2" en
    151 candidat le système le considère en un seul bloc et échappe l'espace entre les
    152 deux :
    153 
    154 ```
    155 a cmd1\ cmd2
    156 ```
    157 
    158 Pour y remédier il faut utiliser l'option `-Q` :
    159 
    160 ```
    161 compadd -Q "cmd1 cmd2"
    162 ```
    163 
    164 Qui auto-complètera :
    165 
    166 ```
    167 a cmd1 cmd2
    168 ```
    169 
    170 A l'éxécution la commande `a` comprendra bien `cmd1` et `cmd2` comme deux
    171 arguments différents.
    172 
    173 Cette astuce est utilisée au tout début de la vidéo du résultat pour
    174 automatiquement ajouter `nom-bdd creer` en l'absence de bdd existante.
    175 
    176 #### Ne pas trier automatiquement les candidats avec `-J arg -o nosort`
    177 
    178 Par défaut le système d'auto-complétion trie les candidats par ordre
    179 alphabétique. Si ce n'est pas souhaitable et qu'on veut les afficher dans
    180 l'ordre fourni à `compadd` il faut associer l'option `-o nosort` avec l'option
    181 `-J` qui prend un argument. `-o` ne fonctionne pas seul, il faut créer un
    182 "groupe" de candidat avec l'option `-J` en écrivant, par exemple, `-J a`. Je
    183 n'ai pas eu besoin d'utiliser les groupes de candidats donc je n'en sais pas
    184 plus.
    185 
    186 Avec la commande `compadd -J a -o nosort b c a` le système nous proposera les
    187 candidats dans l'ordre `b c a` plutôt que `a b c`.
    188 
    189 #### Décorréler l'affichage des candidats de l'argument auto-complété avec `-ld`
    190 
    191 Par défaut ce qui s'affichage dans le menu d'auto-complétion est identique à la
    192 valeur des candidats. Si l'on a pour candidats `a b c` le mnu proposera `a b c`
    193 et notre choix complètera `a`, `b` ou `c`.
    194 
    195 Il est parfois utile de décorréler ce qui est affiché dans le menu de choix et
    196 ce qui est réellement complété. Pour cela il faut associer les options
    197 `-l` et `-d`. `-l` demande à ce qu'un seul candidat soit affiché par ligne et
    198 `-d` permet de renseigner un tableau de valeurs séparées par des espaces. Le
    199 système va associer une à une les valeurs de ce tableau et les candidats qu'il a
    200 récupéré en argument de `compadd`. Le menu de choix affichera visuellement le
    201 contenu du tableau `-d` mais ce sera les candidats associés qui seront ajoutés
    202 dans la ligne de commande. Ainsi avec le tableau et l'appel à `compadd` suivants :
    203 
    204 ```
    205 desc="(
    206     creer\ \ \ \ --\ Créer\ une\ nouvelle\ base\ de\ donnée
    207     ajouter\ \ --\ Ajouter\ une\ dépense\ ou\ une\ personne
    208     retirer\ \ --\ Retirer\ une\ dépense\ ou\ une\ personne
    209     lister\ \ \ --\ Afficher\ le\ contenu\ de\ la\ bdd
    210     calculer\ --\ Calculer\ qui\ doit\ combien\ à\ qui
    211 )"
    212 
    213 compadd -J a -o nosort -ld $desc creer ajouter retirer lister calculer
    214 ```
    215 
    216 Le système proposera visuellement l'option `creer    -- Créer une nouvelle base
    217 de donnée` pour le candidat `creer` et ainsi de suite. Puisque les éléments du
    218 tableau du menu de choix sont séparés par des esapces il faut bien en échapper
    219 les espaces.
    220 
    221 Cette technique est utilisée à deux reprises dans la vidéo du résultat, d'abord
    222 pour les commandes vues dans l'exemple ici, ensuite pour sélectionner la dépense
    223 à retirer (l'utilisateurice navigue dans la bdd ligne par ligne mais uniquement
    224 l'identifiant de la dépense est auto-complété).
    225 
    226 #### Ne pas supprimer les candidats doublons avec `-2`
    227 
    228 Par défaut le système supprime les candidats doublons. Si l'on fait `compadd 1 1
    229 2` seul deux candidats seront proposés, `1` et `2`. Il peut être utile de ne pas
    230 les dédoublonner, en particulier en combinaison avec l'option précédente `-d`.
    231 On peut par exemple vouloir proposer le retrait d'un élément qui apparaît à
    232 plusieurs endroits dans une base de donnée sous plusieurs formes différentes. Il
    233 faut donc que le candidat puisse apparaître plusieurs fois pour être associé
    234 correctement au tableau du menu de choix. Pour ne pas dédoublonner il faut
    235 utiliser l'option `-2` :
    236 
    237 ```
    238 desc="(
    239     dépense1\ blabla\ truc
    240     dépense1\ bidule\ machin
    241     dépense2\ chouette\ aaaa
    242 )"
    243 compadd -J a -2 -ld $desc 1 1 2
    244 ```
    245 
    246 A noter que, comme l'option `-o`, cette option nécessite l'option `-J`. Cette
    247 option est utilisée lors du retrait d'une dépense, vers la seconde 52 de la
    248 vidéo de résultat. On y voit trois options dans le menu dont les deux premières
    249 sont associée à deux candidats de valeurs `1`. Avec dédoublonnage cela n'aurait
    250 pas été possible.
    251 
    252 ### Les variables d'environnements
    253 
    254 #### Savoir à quel numéro d'argument on en est avec `$CURRENT`
    255 
    256 Dans la variable `$CURRENT` se trouve le numéro du mot que l'on est en train
    257 d'écrire/auto-compléter. Le décompte commence à 1 et le premier mot est toujours
    258 la commande en cours. Le premier paramètre (argument ou option, peu importe) est
    259 donc à 2 et ainsi de suite. En utilisant `compadd -x` pour visualiser la valeur
    260 de la variable en dessous de la commande :
    261 
    262 <video src="current-auto.mp4" controls loop loading=lazy>
    263 
    264 On peut utiliser le contenu de cette variable pour faire
    265 varier l'auto-complétion, voir l'exemple ci-dessous.
    266 
    267 #### Connaître la valeur d'un mot déjà rempli avec `$words`
    268 
    269 Il peut être utile de savoir quelle est la valeur d'un paramètre déjà écrit.
    270 Pour cela on peut lire dans le tableau `$words` à l'indice correspondant. Le
    271 premier éléments (`$words[1]`) est toujours le nom de la commande en cours, le
    272 second (`words[2]`) le premier paramètre etc. En utilisant la même astuce que
    273 précédemment :
    274 
    275 <video src="words-auto.mp4" controls loop loading=lazy>
    276 
    277 ### Tout mettre ensemble, l'exemple de tricount
    278 
    279 Voyons comment utiliser tout ça pour reconstruire l'auto-complétion de
    280 `tricount`. Je commente le code ligne par ligne en passant vite sur les aspects
    281 purement shell.
    282 
    283 D'abord, déclarer la fonction au nom `_commande` et inscrire quelle commande
    284 elle auto-complète :
    285 
    286 ```
    287 #compdef tricount
    288 _tricount() {
    289 ```
    290 
    291 Ensuite écrire éventuellement en dur l'endroit où se trouve les bases de données
    292 et récupérer la valeur de la base courante, de l'action à mener dessus et
    293 l'objet :
    294 
    295 ```
    296 local BDDFOLDER="/srv/tricount"
    297 local curbdd="$words[2]"
    298 local action="$words[3]"
    299 local objet="$words[4]"
    300 ```
    301 
    302 Puisque cette fonction est appelée à chaque fois que la touche tabulation est
    303 lancée les variables `curbdd`, `action` et `objet` peuvent très bien être vides
    304 parce que la liste `$words` n'est pas encore remplie.
    305 
    306 Si on est au deuxième argument c'est que l'on cherche à choisir une base de
    307 donnée. Il faut récupérer la liste en regardant dans le dossier correspondant.
    308 S'il y en a au moins une on les ajoute en tant que candidats avec un message
    309 explicatif.  S'il n'y en a pas on peut pré-remplir la commande pour en créer une
    310 avec un nom bidon modifiable :
    311 
    312 ```
    313 if [ "$CURRENT" = 2 ];then
    314     bdds=$(find $BDDFOLDER -type f | cut -d'/' -f4 | sort)
    315     if [ "$bdds" ];then
    316         compadd -X "Choisir une base de donnée" $bdds
    317     else
    318         compadd -Qx "Aucune bdd de dispo, go en créer une" "nom-bdd creer"
    319     fi
    320 fi
    321 ```
    322 
    323 Si on est au troisième argument c'est que l'on cherche à effectuer une commande
    324 sur une bdd existante ou pas. Si la bdd n'existe pas on ajoute techniquement
    325 l'action `creer` en tant que candidat. Sinon on prépare les variables contenant
    326 les candidats et leurs descriptions en faisant usage de `-l` et `-d`.
    327 
    328 ```
    329 if [ "$CURRENT" = 3 ];then
    330     if ! [ -f $BDDFOLDER/$curbdd ];then
    331         compadd creer
    332     else
    333         actions=(ajouter retirer lister calculer)
    334         desc="(
    335             ajouter\ \ --\ Ajouter\ une\ dépense\ ou\ une\ personne
    336             retirer\ \ --\ Retirer\ une\ dépense\ ou\ une\ personne
    337             lister\ \ \ --\ Afficher\ le\ contenu\ de\ la\ bdd
    338             calculer\ --\ Calculer\ qui\ doit\ combien\ à\ qui
    339         )"
    340         compadd -X "Choisir une action" -J a -o nosort -ld $desc $actions
    341     fi
    342 fi
    343 ```
    344 
    345 Si on est au quatrième argument et que l'action (le troisième) est `ajouter` ou
    346 `retirer` alors on propose un objet à ajouter ou retirer :
    347 
    348 ```
    349 if [ "$CURRENT" = 4 ] && { [ "$action" == "ajouter" ] || [ "$action" == "retirer" ]; };then
    350     compadd -X "Choisir un objet à $action" depense personne
    351 fi
    352 ```
    353 
    354 Si l'action est `retirer` et l'objet `depense` alors on récupère les
    355 identifiants des dépenses et on construit ensuite un tableau pour l'affichage du
    356 menu en réorganisant les lignes de dépenses de la bdd et en échappant les
    357 espaces[^7]. Finalement on ajoute les identifiants en tant que candidats et les
    358 lignes de dépenses en tant qu'option visuelles. Aussi on retire le tri par
    359 défaut qui mélangerait toutes les dépenses et on demande à ne pas supprimer les
    360 candidats doublons. Ainsi les éventuelles multiples lignes pour une dépense
    361 donnée auront bien l'identifiant de la dépense en candidat en face.
    362 
    363 ```
    364 if [ "$action" = "retirer" ] && [ "$objet" = "depense" ];then
    365     ids=($(< "$BDDFOLDER/$curbdd" cut -f5 | sed 1d | paste -s -d' '))
    366     # Ex: ids="1 1 2"
    367     desc="(
    368         $(< "$BDDFOLDER/$curbdd" awk 'BEGIN{OFS="\t"};NR>1{print $5,$1,$2,$3,$4}' | column -ts'	' | sed 's# #\\ #g')
    369     )"
    370     # Ex: desc="(
    371     #     dépense1\ blabla\ truc
    372     #     dépense1\ bidule\ machin
    373     #     dépense2\ chouette\ aaaa
    374     # )"
    375     compadd -X "Choisir une dépense à retirer" -J a -2 -o nosort -ld $desc $ids
    376 fi
    377 ```
    378 
    379 Si l'action est `retirer` et l'objet de retrait une `personne` alors on récupère
    380 la liste des personnes dans la base de donnée. Si la liste est vide on affiche
    381 un message d'erreur comme quoi il n'y a personne à retirer, sinon on ajoute la
    382 liste en candidats :
    383 
    384 ```
    385 if [ "$action" = "retirer" ] && [ "$objet" = "personne" ];then
    386     personnes=($(< "$BDDFOLDER/$curbdd" head -n1 | tr ' ' '\n' | sed 1d | sort))
    387     if ! [ "$personnes" ];then
    388         compadd -x "Il n'y a aucune personne à retirer"
    389     else
    390         compadd -X "Choisir une personne à retirer" -o nosort $personnes
    391     fi
    392 fi
    393 ```
    394 
    395 Si l'action est `ajouter` et l'objet une `depense` alors on récupère la liste
    396 des personnes de la base de donnée :
    397 
    398 ```
    399 if [ "$action" = "retirer" ] && [ "$objet" = "personne" ];then
    400     personnes=($(< "$BDDFOLDER/$curbdd" head -n1 | tr ' ' '\n' | sed 1d | sort))
    401 ```
    402 
    403 Puis on vérifie à quelle étape de la construction de la dépense on
    404 est. Si l'on est au paramètre 5 c'est le début et on cherche à ajouter la
    405 personne qui paye. S'il n'y a pas de candidats disponible on peut afficher un
    406 message d'erreur :
    407 
    408 ```
    409 if [ "$CURRENT" = 5 ];then
    410     if ! [ "$personnes" ];then
    411         compadd -x "Pas de personnes disponibles :("
    412         compadd -x "Vous pouvez tout de même entrer un nom, ça fonctionnera"
    413     else
    414         compadd -X "Choisir la personne qui paye" $personnes
    415     fi
    416 fi
    417 ```
    418 
    419 Si l'on est à l'argument 6 on cherche à ajouter un montant :
    420 
    421 ```
    422 [ "$CURRENT" = 6 ] && compadd -X "Choisir un montant" -J a -o nosort $(seq 1 50)
    423 ```
    424 
    425 Si l'on est à l'argument 7 on cherche à ajouter une raison pour la dépense. Il
    426 est possible de faire un peu de pré-traitement sur les candidats pour qu'ils
    427 évoluent avec la base de donnée. Ici les raisons apparaîtront dans l'ordre de la
    428 plus utilisée à la moins utilisée avec quelques raisons d'exemple en bonus à la
    429 fin :
    430 
    431 ```
    432 if [ "$CURRENT" = 7 ];then
    433     raisons=($(< "$BDDFOLDER/$curbdd" cut -f4 | sed 1d | sort | uniq -c | awk '{print $2}' | paste -s -d' '))
    434     compadd -X "Mettre une raison" -J a -o nosort $raisons repas transport courses ...
    435 fi
    436 ```
    437 
    438 Puis finalement si l'on est à l'argument 8 on chercher à ajouter les
    439 bénéficiaires de la dépense. On peut afficher plusieurs lignes de messages en
    440 multipliant les appels à `compadd -x`. On peut ajouter "à la main" un candidat
    441 en plus de ceux récoltés dans une liste comme le candidat `toustes` ici :
    442 
    443 ```
    444 if [ "$CURRENT" -ge 8 ];then
    445     if ! [ "$personnes" ];then
    446         compadd -x "Pas de personnes disponibles :("
    447         compadd -x "Vous pouvez tout de même entrer un nom, ça fonctionnera"
    448     else
    449         compadd -X "Choisir une ou plusieurs personnes bénéficiaires" $personnes toustes
    450     fi
    451 fi
    452 ```
    453 
    454 Cet exemple n'est pas parfait. On pourrait factoriser un certain nombre de
    455 choses et sortir de la fonction rapidement après avoir ajouté des candidats
    456 plutôt que de faire tous les tests alors même que l'on sait qu'ils seront faux.
    457 Il est ici à titre d'exemple.
    458 
    459 ## Installation
    460 
    461 Pour activer l'auto-complétion `zsh` dans toutes ces sessions il faut ajouter
    462 les lignes suivantes dans son fichier `~/.zshrc`[^3] :
    463 
    464 ```
    465 autoload -Uz compinit
    466 compinit
    467 # Pour pouvoir parcourir le menu des suggestions
    468 # avec les flèches comme dans les vidéos
    469 zstyle ':completion:*' menu yes select
    470 ```
    471 
    472 Il faut ensuite écrire la fonction d'auto-complétion avec ceci pour toute
    473 première ligne :
    474 
    475 ```
    476 #compdef cmd
    477 _cmd() {
    478     [...]
    479 }
    480 ```
    481 
    482 Cette première ligne est un "faux" commentaire permettant à `zsh` de savoir que
    483 la fonction qui suit doit être utilisée pour compléter la commande `cmd` comme
    484 si l'on avait lancé `compdef _cmd cmd` à la main.
    485 
    486 Finalement il faut installer le fichier, préférablement nommé `_cmd` dans le
    487 dossier `/usr/share/zsh/functions/Completion/Unix/_cmd`[^8]. Au lancement `zsh`
    488 lit tous les fichiers présents dans ces dossiers, regarde la première ligne et
    489 fait l'association entre la fonction de complétion et la commande.
    490 
    491 ## De l'auto-complétion personnalisée ?
    492 
    493 L'un des mécanismes principaux pour faciliter la découvrabilité et l'usage du
    494 shell, notamment des commandes les plus complexes, est de restreindre l'espace
    495 d'exploration. C'est ce qui est par exemple fait dans [cet
    496 article](https://dl.acm.org/doi/10.1145/3332165.3347944)[^12] via un système capable
    497 de parser un ensemble restreint de commandes shell, sélectionné par un·e
    498 experte, et rendant une GUI exposant les options et arguments utilisé.es
    499 par ce sous-ensemble plutôt que la totalité des fonctionnalités de la commande.
    500 
    501 C'est également en partie l'idée derrière
    502 [`zenu`](http://arthur.bebou.netlib.re/zenu), à savoir mettre des commandes
    503 parfois complexes derrière des menus pour faciliter la navigation, l'usage et le
    504 partage de ces commandes tout en diminuant la charge cognitive.
    505 
    506 Malheureusement les fonctions d'auto-complétion fournies par défaut avec `zsh`
    507 laissent à désirer de côté là. Par exemple lorsque l'on auto-complète la
    508 commande `convert` d'image-magick après avoir écrit un `-` dans `zsh` on obtient
    509 :
    510 
    511 ![Un terminal, texte blanc sur fond noir, avec une liste assez intimidante de
    512 l'auto-complétion zsh de la commande convert d'image magick. Il y a vraiment
    513 beaucoup d'options](convert.jpg)
    514 
    515 C'est sympa d'avoir des petites descriptions mais la taille de la liste est
    516 intimidante et rend la complétion assez peu utile. Ce n'est pas un diss contre
    517 les contributeurices des fonctions de complétion de `zsh`, cela s'explique par
    518 le fait que :
    519 
    520   1. `zsh` est utilisé par des (dizaines de ?) millions de personne dans le
    521      monde. Il n'est donc pas possible de créer une fonction de complétion qui
    522      convienne à tout le monde. À défaut la philosophie retenue semble être de
    523      faire une fonction assez peu directive mais exhaustive.
    524   2. Image-magick comporte un nombre incalculable de fonctionnalités (peut-être
    525      trop).
    526 
    527 J'émets l'hypothèse qu'en adoptant une approche située, en sachant pour *qui* et
    528 *quels usages* on créé *les* fonctions de complétion, il serait possible d'en
    529 faire des outils pédagogiques intéressants. Il n'y aurait pas *une* fonction
    530 d'auto-complétion exhaustive mais ne satisfaisant réellement personnes mais une
    531 multitude de fonctions que l'on se partagerait selon nos pratiques ou notre
    532 niveau de familiarité avec l'outil.
    533 
    534 Bien sûr cela nécessite d'écrire du code mais c'est pour ça que je fais ce tuto
    535 :)
    536 
    537 ## Références
    538 
    539 La doc `zsh` de référence à propos de `compadd` est ici :
    540 https://zsh.sourceforge.io/Doc/Release/Completion-Widgets.html#Completion-Builtin-Commands
    541 
    542 [^1]: `yt-dlp -h | wc -l` = 892. Et en plus faut attendre deux secondes pour que ça s'affiche.
    543 [^2]: Par moderne j'entends sous la forme que l'on utilise aujourd'hui depuis [au moins la version de `zsh` datant de 1999](https://github.com/zsh-users/zsh/commit/e74702b467171dbdafb56dfe354794a212e020d9#diff-ce02ccc8e9de1903cddd3276d6b5b986c204822c6c6d7d4b22f6db73d77ea816R1) et [depuis bien plus longtemps de manière plus primitive dans d'autres shells](https://developer.ibm.com/tutorials/l-linux-shells/).
    544 [^3]: Ou tout autre endroit qui est sourcé à la création de chaque shell
    545     interactif, si vous sachez vous sachez
    546 [^4]: aussi connue sous le nom de la touche "flèche flèche"
    547 [^5]: en `zsh` pas besoin du `;` de fin quand on écrit des fonctions sur une
    548     seule ligne mais en, team posix ici
    549 [^6]: la documentation `zsh` précise que `compadd` est assez "bas niveau" et
    550     qu'il est souvent préférable d'utiliser les très nombreuses autres fonctions
    551     proposées par `zsh` wrappant `compadd`. Le souci est qu'elles sont très
    552     nombreuses, compliquées et qu'elles sont surtout utiles pour faire de
    553     l'auto-complétion de commandes respectant des conventions bien établies
    554     (combo d'option courtes et longues `-` `--`, certaines des flags, d'autres
    555     suivies d'une chaÎne de caractère, parfois des fichiers, parfois des IP
    556     etc). Puisque l'on fait un peu n'imp avec `tricount` et que la documentation
    557     est assez opaque j'ai trouvé qu'il était plus facile d'apprendre uniquement
    558     `compadd` en partant du principe que tout était possible avec.
    559 [^7]: Un peu de quoting hell ici puisque je connais très mal zsh et ses
    560     structures de données. Je pense qu'on peut faire mieux
    561 [^8]: du moins pour Debian, probablement pour tous les linux.
    562 [^9]: J'utilise "auto-complétion" et "complétion" de manière interchangeable
    563 [^10]: Ou carrément exécuter des commandes pour lancer un logiciel ou je ne sais
    564     trop quoi d'autre. C'est étrange parce que ce n'est clairement pas ce qui
    565     est attendu par les utilisateurices mais peut-être que ça peut convenir à
    566     votre manière de travailler.
    567 [^12]: A propos duquel je pense écrire un article de blog bientôt d'ailleurs