Il y a un poisson dans ma coquille !

Cela fait désormais quelques jours que j'ai changé mon shell de base pour fish en lieu et place de zsh. Un peu perplexe lorsque j'en ai entendu parler la première fois chez Xavier, un peu plus lorsque mon boss m'en reparle, j'ai finalement pris le pas de tester le bestiau et sans trop divulgacher la suite, je peux vous dire que de la balle.

Comme beaucoup de monde, je suis passé de bash à zsh par l'intermédiaire du projet Oh my ZSH : très pratique car c'est un paramétrage très arrêté de zsh avec énormément de plugins activables au besoin. Ceci dit, le projet a grossi, bien trop à mon avis et la maintenance de tout ce paramétrage en devient compliquée et longue à la détente. La mise à jour des complétions pour certains outils qui évoluent vite (je pense à Docker pendant sa croissance) peinent à être mergées, le choix et l'unification des aliases au sein de la communauté, tout cela prends du temps et certains choix faits ne sont pas forcément toujours compatibles ou en adéquation avec ceux que a fait localement.

C'est pourquoi, lorsqu'on m'a parlé des merveilles de fish dans sa version distribuée de base, cela a bien résonné dans ma tête. Mais alors qu'est-ce qui différencie fish de bash ou zsh (pour ne citer que ceux par lesquels je suis passé auparavant) ?

Ce qui ressemble à ce que je connais de bash/zsh

Ce qu'on attend d'un shell moderne est là : autocomplétion, historique et rappel de commandes, prompt, aliases et fonctions personnalisés. Tout y est, mais pas forcément tel qu'on s'y attend. En effet, des éléments sus-cités ce sont les 2 premiers qui sautent aux yeux dès la première commande tapée.

Colorisation syntaxique de la ligne de commande

La ligne de commande intègre de base une colorisation syntaxique permettant d'identifier rapidement si une commande existe ou non, si une chaîne passée en paramètre est correctement close, les variables utilisées. C'est très agréable et d'autant plus pénalisant de retourner ensuite sur un shell d'une autre machine n'étant pas configurée de la sorte.

Autosuggestion de commandes

Deuxième chose qui étonne, le pseudo-remplissage de la ligne de commande au fur et à mesure de la frappe. Dans la mesure du possible, fish va en effet proposer de compléter la frappe de la commande courante avec le nom d'un exécutable, voire du premier argument en se basant sur l'hitorique. Ensuite, on peut valider ou non la proposition avec la flèche droite ou Ctrl-f. La complétion des arguments avec Tab est également de mise et chose très agréable, s'appuie sur l'ensemble des man installé. On peut d'ailleurs regénérer/mettre à jour ce système avec la commande fish_update_completions.

Complétions avec Tab

Ce qui est complètement différent

Là ou ça se corse, c'est sur la partie configuration/personnalisation. Le concept d'alias n'existe pas en fish, tout est fonction. Bon ok, il y a bien un sucre syntaxique pour imiter le comportement d'un alias, mais au final c'est bien une fonction qui est générée. Cela étant dit, rien de bien compliqué à cela, mise à part peut-être l'absence des opérateurs de contrôle && et || remplacé par ; and et ; or, au fond bien plus lisibles (tout comme la syntaxe de programmation générale) mais j'avoue avoir eu un peu de mal à perdre le réflexe.

La gestion du prompt est elle aussi différente mais bien plus intéressante. On ne gère pas sa construction dans une variable d'environnement comme en bash, mais avec… fonctions ! Et évidemment pour le construire on peut appeler des sous-fonctions, mettre des conditions et il est donc possible d'avoir un prompt très paramétrable et réactif en fonction des cas.

Migration de zsh, et particulièrement depuis oh-my-zsh

Alors évidemement lorsqu'on a ses habitudes, et a fortiori de longue date, changer c'est compliqué. Personnellement j'aime avoir un prompt et un thème simple, et dans ce domaine c'est pure avec lequel j'évolue depuis quelques temps désormais. Heureusement pour moi, je ne suis pas seul à apprécier ce prompt, et j'ai trouvé un port de mon prompt préféré. Bon, je l'ai quelques peu modifié pour qu'il affiche systématiquement le temps d'exécution des commandes de plus de 10s mais rien de plus.

Le meilleur prompt du monde

Ensuite pour mes commandes habituelles, merci la commande alias qui permet de définir rapidement des fonctions simples agissant tels des aliases, comme par exemple :

alias be='bundle exec'
alias rails='be rails'
alias rc='rails console'
alias rdm="rails db:migrate"

alias ga='git add'
alias gbr='git branch --remote'
alias current_branch='git symbolic-ref -q --short HEAD'

Et pour les fonctions, même principe qu'en bash, mis à part la syntaxe des arguments ($argv) et des appels d'autres fonctions (() au lieu de $()).

function rename_branch -d "Rename current git branch to new name"
  git branch -m (current_branch) $argv[1]
end

En ce qui concerne les extensions de oh-my-zsh, j'ai essayé de faire en sorte de m'en débarrasser complètement pour garder la légèreté de fish, et au final simpler is better.

Pour conclure ce billet qui commence à s'allonger un peu trop, je suis plutôt satisfait de ce changement. Mes habitudes ont finalement peu changé et j'y ai gagné une meilleure autocomplétion, qui se plante bien moins que sous oh-my-zsh. Je gage qu'il me reste encore bien des choses à découvrir avec ce shell et entre nous c'est bien ce que j'apprécie le plus dans notre domaine !

Un grand merci à Xavier et à Clément pour m'avoir mis le pied à l'étrier.