Snippets — explication
De quoi s'agit-il ?
Un menu Snippets dont le contenu vient d'un fichier TOML, et un snippet choisi déposé dans le fichier que vous éditez. Cette page traite des décisions qui lui donnent sa forme : pourquoi le menu est reconstruit à chaque ouverture, pourquoi l'éditeur a gagné de vrais sous-menus pour lui, pourquoi l'insertion réindente — et, pour ce qui n'appartient qu'à Turbo JS, pourquoi les snippets de départ sont écrits comme ils le sont.
Pourquoi le menu est construit au moment où il s'ouvre
Tous les autres menus de l'éditeur sont décidés une fois, dans New(). Celui-ci ne peut pas l'être, et pour deux raisons indépendantes.
La première est le fichier. Les snippets vivent dans du TOML, et tout l'intérêt est que vous l'éditiez — souvent dans cet éditeur, dans la fenêtre que l'entrée Create snippets file vient d'ouvrir pour vous. Un menu construit au démarrage montrerait l'état du fichier au lancement, et il faudrait redémarrer pour voir un snippet qu'on vient d'écrire. C'est le genre de friction qui fait qu'une fonctionnalité n'est pas utilisée du tout.
La seconde est la fenêtre au premier plan. Le menu est filtré par ce que vous éditez, donc il change quand vous appuyez sur F6. Il n'existe aucun instant du démarrage où la réponse existe.
ui.Menu a donc gagné un champ OnOpen : une fonction que la barre appelle juste avant de dérouler un menu, laissant son propriétaire regarnir Items d'abord. C'est le même mécanisme de communication ascendante que partout ailleurs dans ce code — un champ fonction, pas une interface — et il s'exécute exactement au moment où le contenu va être vu, pas plus souvent.
Pourquoi l'éditeur a gagné des sous-menus
ui.MenuItem ne savait pas imbriquer, et l'ajouter a été la plus grosse pièce de ce travail : un second panneau à placer et à dessiner, des flèches qui signifient « plus profond » et « ressortir », le pointeur qui ouvre une branche au survol et la referme en la quittant, et une fermeture qui range les deux panneaux d'un coup.
L'alternative était un seul panneau plat avec les groupes en intitulés grisés entre des filets. Cela fonctionne, ne demande rien de neuf, et s'effondre sur le cas même pour lequel la fonctionnalité existe : un projet de trente snippets donne un menu plus haut que le terminal. Un regroupement qui étiquette sans replier ne résout pas le problème qu'il semble résoudre.
C'est délibérément un seul niveau. Le format est des groupes contenant des snippets — exactement un niveau — et une profondeur générale supposerait de remplacer les deux indices de la barre par un chemin, dans le widget dont dépendent déjà tous les dialogues et tous les tests de menu. C'est du travail spéculatif sur la partie la plus porteuse de l'interface.
Deux détails du sous-menu méritent d'être nommés, parce qu'ils ont été choisis et non subis :
- Droite et gauche sont asymétriques avec Échap. Droite ouvre une branche, ou passe au menu suivant quand l'entrée n'en a pas : elle signifie donc toujours « plus profond », où que l'on soit. Gauche ressort d'un sous-menu vers son parent, tandis qu'Échap referme tout le menu — parce qu'annuler doit vouloir dire annuler, de n'importe où.
- Le panneau bascule à gauche, et sa largeur est aussi bornée. Un sous-menu qui dépasserait le bord droit est dessiné de l'autre côté de son parent. Basculer ne suffit pas : un panneau plus large que le terminal ne peut pas être rendu visible en le déplaçant, donc la largeur est bornée aussi et les intitulés longs sont coupés par le peintre. Un cadre sans bord droit paraît cassé d'une façon dont un intitulé tronqué ne l'est pas.
Pourquoi l'insertion réindente
Un snippet est du texte, et l'implémentation évidente est de l'insérer. C'est juste pour une seule ligne et faux pour tout le reste, c'est-à-dire pour l'essentiel de ce que les gens gardent en snippets.
Déposé tel quel, un corps multi-ligne repart en colonne zéro. Inséré dans une fonction, dans une boucle for, dans un then — là où l'on insère justement un try / catch — le résultat est un texte qu'aucun lecteur n'accepte. JavaScript, lui, s'en contente — l'indentation n'y porte aucun sens, contrairement à Python — et Prettier saurait le remettre d'équerre après coup, mais au prix d'un passage d'outil sur tout le fichier. La première chose qu'on fait est de le réindenter à la main, et une fonctionnalité dont la sortie doit être corrigée chaque fois ne fait gagner de temps à personne.
Les lignes après la première reçoivent donc l'indentation de la ligne où était le curseur. Cela recopie ce que le fichier emploie déjà — tabulations ou espaces, en telle quantité — plutôt que d'imposer un choix, ce qui compte dans un projet à l'histoire mêlée.
Deux décisions plus petites à l'intérieur :
- Une ligne vide du corps reste vide. La compléter jusqu'à l'indentation y mettrait des espaces en fin de ligne, que Prettier supprime ensuite — du bruit dans le diff de l'enregistrement suivant.
- C'est une seule annulation. Un snippet est une seule action pour qui l'a choisi, donc
Ctrl-Zdoit tout reprendre. Cela découle de faire toute l'insertion en un seulReplaceRange, la règle que le buffer impose déjà à toute autre modification.
Les emplacements et les tabulations successives — ${1:nom} et le passage de l'un à l'autre — ont été envisagés et laissés de côté. C'est une seconde fonctionnalité, avec son propre état à maintenir à travers les modifications, alors que ce qui était demandé est du texte réutilisable.
Pourquoi deux fichiers, et pourquoi le projet gagne
Vos snippets vous appartiennent et doivent vous suivre d'un projet à l'autre ; ceux d'un projet lui appartiennent et doivent arriver avec un clone. Ni l'un ni l'autre n'est la réponse complète, donc les deux sont lus.
Quand un nom entre en conflit dans le même groupe, celui du projet remplace le vôtre. C'est le plus spécifique des deux énoncés, et c'est celui dont une équipe a convenu — la même raison qui fait qu'un drapeau -theme l'emporte sur le réglage d'un projet, tandis que le réglage d'un projet l'emporte sur le défaut intégré.
Pourquoi un fichier illisible est bruyant
Une faute de frappe dans le TOML pourrait faire disparaître tous les snippets en silence et laisser un menu ne contenant que Create snippets file — ce qui ressemble exactement à un projet sans snippets, et vous envoie créer un fichier que vous avez déjà.
Le menu affiche donc un Cannot read snippets grisé là où les groupes seraient. Il ne peut pas être choisi, il est là où vous regardiez, et l'entrée de création reste en dessous : il y a une issue dans les deux cas.
Pourquoi les snippets de départ sont indentés de deux espaces
Ceci n'appartient qu'à Turbo JS. Le fichier de départ que Create snippets file écrit contient sept snippets JavaScript — import, require, async function, try / catch, class, for of, test — et un snippet JSON, scripts, et il fallait choisir comment indenter leurs corps.
Dans un éditeur dont le langage a un formateur, la réponse est imposée : on écrit ce que le formateur écrirait, sinon le premier passage de l'outil réécrit le snippet. JavaScript en a un, Prettier, que JavaScript ▸ Format lance sur tout le projet, et Prettier écrit deux espaces par défaut. C'est donc deux espaces, parce qu'une tabulation serait reformatée au premier npx prettier --write — un diff que personne n'a demandé. Un test tient les corps à cette règle.
Pourquoi les corps sont des chaînes basiques TOML
Même raison d'être locale. Une chaîne JavaScript porte \n et \" comme une chaîne Go, et un corps qui en contiendrait — console.error("caught: \"" + e + "\"") — ne survivrait pas à une chaîne TOML basique, """…""" : ces échappements seraient résolus par l'analyseur TOML avant que l'éditeur ne voie le corps, et le snippet arriverait dans le fichier avec un vrai guillemet là où le code voulait un guillemet échappé.
Aucun des corps de départ n'en contient — ils nomment des modules, "node:test", et n'échappent rien — et ils sont donc écrits en chaînes basiques, """…""". Le jour où l'un d'eux aura besoin d'une barre oblique inverse, il passera en chaîne littérale, '''…''', où une barre oblique inverse est une barre oblique inverse : ce qui est dans le fichier de snippets doit être exactement ce qui arrive dans le fichier JavaScript, ce qui est la seule chose qu'on attend d'un snippet.
Liens avec le reste
- Toutes les clés et toutes les règles : Référence des snippets
- Les mettre en place : Insérer des snippets depuis un menu
- L'autre fichier du même dossier : Réglages de projet
- Les noms de langages qu'emploie
languages: Langages colorés
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
|