12 points par GN⁺ 2025-08-13 | 1 commentaires | Partager sur WhatsApp
  • Présentation d’une méthode pour implémenter une fonctionnalité d’autocomplétion par tabulation avec affichage de descriptions, y compris pour des mots déjà complétés, dans Bash et Zsh
  • Bash et Zsh utilisent des API d’autocomplétion différentes, et seul Zsh fournit nativement l’affichage des descriptions dans l’autocomplétion
  • La structure consiste à générer les candidats avec _generate_foo_completions, puis à les renvoyer via COMPREPLY dans Bash et compadd dans Zsh
  • Pour reproduire dans Bash la fonctionnalité de description de Zsh, on inclut la description dans la chaîne candidate, puis on la supprime uniquement lorsqu’il n’y a qu’un seul candidat
  • Même en présence d’un seul candidat, on améliore le comportement en ajoutant volontairement une ambiguïté afin d’afficher la description lors de l’appui sur <TAB>
  • Le script final offre la même expérience utilisateur dans les deux shells et prend en charge la complétion partielle, la complétion totale et l’affichage des descriptions des candidats

Contexte du problème

  • L’autocomplétion par tabulation (tab-completion) est utile pour explorer des commandes ou des flags, en particulier lorsqu’on découvre une API ou un outil CLI
  • Par défaut, Zsh n’affiche les descriptions que lorsqu’il y a plusieurs candidats, et Bash ne le permet qu’au prix d’une configuration supplémentaire
  • Mais pour un mot déjà entièrement complété, aucun des deux shells n’affiche de description
  • L’utilisateur doit donc passer par une procédure fastidieuse pour voir la description :
    • supprimer quelques caractères pour faire correspondre plusieurs candidats
    • appuyer sur la touche <TAB> pour afficher la liste des candidats
    • repérer visuellement la description voulue
    • retaper les caractères supprimés pour exécuter la commande

Aperçu de la solution

  • Même lorsqu’il n’y a qu’un seul candidat, on ajoute un candidat factice (dummy completion) pour créer une ambiguïté
  • Cela permet à Bash comme à Zsh d’afficher la liste des candidats avec leurs descriptions
  • Il faut toutefois veiller à ce que le texte de description ne soit pas inséré dans la commande réelle

Concepts de base

  • Lorsqu’on appuie sur <TAB>, l’autocomplétion reçoit le mot courant et la position du curseur, puis renvoie une liste de candidats possibles
  • Bash : affectation des candidats dans le tableau COMPREPLY
  • Zsh : enregistrement des candidats via la commande compadd
  • La fonction _generate_foo_completions affiche les chaînes candidates et peut, en pratique, les générer dynamiquement selon l’état du CLI

Prise en charge simultanée de Bash et Zsh

  • Implémentation adaptée à chaque shell avec les fonctions _complete_foo_bash et _complete_foo_zsh
  • Distinction via if [ -n "${ZSH_VERSION:-}" ]; then ... elif [ -n "${BASH_VERSION:-}" ]; then ... fi
  • L’utilisateur enregistre ensuite le script dans .bashrc ou .zshrc avant de l’appliquer

Affichage des descriptions dans Zsh

  • Les chaînes candidates utilisent le format nom: description
  • Zsh : transmission parallèle du nom et de la description via compadd -d raw -- $trimmed
  • Bash : seuls les candidats sans la partie description sont transmis à COMPREPLY (pas de prise en charge native des descriptions)

Implémenter les descriptions dans Bash

  • En cas de candidats multiples, on affiche directement les chaînes contenant les descriptions
  • En cas de candidat unique, on supprime seulement la description
  • On exploite le comportement de Bash, qui n’insère que le préfixe commun lors de l’autocomplétion, afin d’éviter que la description ne soit ajoutée à la saisie réelle

Afficher une description même avec un seul candidat

  • Pour montrer la description même lorsqu’un mot est déjà complété et que l’utilisateur appuie sur <TAB>, on ajoute un candidat factice pour provoquer une ambiguïté
  • Zsh : ajout du candidat factice dans les deux tableaux parallèles (raw, trimmed)
  • Bash : lorsqu’il n’y a qu’un seul candidat, ajout du nom seul dans trimmed

Résultat final

  • En cas de candidats multiples, le nom et la description sont tous deux affichés
  • Même avec un candidat unique, il est possible de consulter la description via <TAB>
  • Bash et Zsh offrent tous deux une expérience identique
  • Exemple d’utilisation :
    $ foo <TAB>  
    apple: a common fruit banana: starchy and high in potassium  
    apricot: sour fruit... cherry: small and sweet...  
    

1 commentaires

 
GN⁺ 2025-08-13
Avis Hacker News
  • Sur fish, si le programme qui vous intéresse fournit une page man, il suffit d’exécuter fish_update_completions
    • Cette commande analyse toutes les pages man du système et génère les fichiers d’autocomplétion dans ~/.cache/fish/generated_completions/
    • Si la page man est insuffisante ou absente, on peut l’écrire soi-même et contribuer en amont
    • Le format de fish est simple, la documentation officielle suffit donc
    • Exemple : curl avec -L → 'Follow redirects', -O → 'Write output to file named as remote file'
    • Quand je partage mon écran, les gens pensent que j’utilise zsh et des plugins, alors qu’en réalité j’utilise fish avec la configuration par défaut, qui est déjà joli
    • En revanche, ce serait bien d’améliorer l’autocomplétion non préfixéecar TAB se développe en blkdiscard, ce qui provoque des dysfonctionnements même si cargo n’est pas dans le PATH
    • Pour les programmes qui n’ont pas de page man et ne proposent que --help, je me demande si fish a un équivalent de _gnu_generic de zsh ou de complete -F _longopt de bash
    • Il existe aussi un script qui génère l’autocomplétion à partir des pages man pour zsh → zsh-manpage-completion-generator
    • Sur OpenSUSE, quand j’ai tapé zypper search fish-completion, plus de 200 paquets sont sortis, ce qui m’a paru un peu louche
  • L’autocomplétion bash devient de plus en plus « intelligente », et c’est gênant qu’elle bloque la complétion des fichiers/répertoires quand elle juge qu’un nom de fichier n’est pas approprié à la position actuelle du curseur
    • À mon avis, il vaudrait mieux revenir systématiquement à la complétion de noms de fichiers en fallback
    • C’est à cause de ça que j’ai déjà envisagé de désactiver complètement les scripts de complétion
    • Bash a des fonctions comme complete-filename, liée à M-/, qui complètent uniquement des noms de fichiers sans tenir compte du contexte
    • Pour certaines commandes, la complétion de fichiers est totalement cassée, donc je contourne le problème en complétant d’abord avec ls, puis en remplaçant la commande
    • Quand le fichier existe mais n’est pas exécutable, un message du type « file foo.exe exists but it isn't executable » serait moins déroutant
    • Le fait qu’après complete -r, tout fonctionne comme souhaité laisse penser que le problème vient des scripts bash-completion
    • C’est une UX frustrante, comparable à la validation frontend d’un formulaire web qui affiche « Invalid email! » dès qu’on a saisi la première lettre d’une adresse
  • C’est un article que j’ai écrit, et j’aimerais que d’autres le lisent avec intérêt
    • Au lieu de charger le script de complétion zsh à chaque fois, on peut l’installer dans $fpath pour qu’il soit mis en cache, ce qui accélère le temps de démarrage
    • Lors d’une distribution via Homebrew, on peut installer automatiquement les completions
    • Les complétions zsh sont vastes et pas faciles à maîtriser, mais au travail je les affine progressivement en ajoutant à notre script wrapper ansible des options par playbook, la complétion des tags, etc.
  • Récemment, j’ai commencé à utiliser la bibliothèque usage pour développer des CLI → intégrée à clap, elle peut générer les completions, l’argparse et les pages man
    • Je me demande encore si cela vaut la peine de remplacer le bloc argparse d’un script fish, mais c’est bien mieux que optparse
    • Je développe moi aussi un outil de définition de spécifications CLI du même genre, et je viens juste d’y ajouter la prise en charge de fish
  • Présentation de fx.wtf, qui prend en charge l’autocomplétion des champs JSON dans bash/zsh
    • C’est plus léger que ijq, mais cela peut être utile selon les cas
  • Tutoriel pour créer un completer avec les fonctions intégrées de zsh → zsh-completions-howto
  • Je me demande s’il existe un flag standard qui évite aux programmes d’avoir à écrire des scripts bash
    • Dans zsh, _gnu_generic permet une complétion simple basée sur --help
    • Il existe plusieurs exemples, comme clap_complete en Rust, l’option --generate de ripgrep, ou la génération de complétion à l’exécution dans PHP Symfony
    • Ce serait bien d’avoir un standard --completion que les bibliothèques de parsing communes implémenteraient automatiquement
  • Dans ksh, on peut implémenter simplement l’autocomplétion avec de simples définitions de tableaux
    • Exemple : si on met des noms de signaux dans le tableau complete_kill_1, on obtient la complétion du premier argument de la commande kill
    • Je me demande si c’est une syntaxe de ksh93 ou si cela a été rétropoté vers oksh
  • Exemple d’un petit snippet de complétion zsh que j’ai créé : dans la fonction set-java-home, j’utilise _describe pour compléter les versions à partir de la liste ~/apps/java/*
    • La structure est très simple, presque un one-liner
  • Comme la fonction principale des LLM est l’autocomplétion de texte, GitHub Copilot gère assez bien la complétion bash et zsh dans le terminal de vscode