Aller au contenu

Coloration syntaxique

Textchum colore le code avec tree-sitter : chaque document dont le langage est reconnu conserve un véritable arbre d'analyse, mis à jour de façon incrémentale à chaque édition, et la coloration se calcule à partir de cet arbre — pas avec des expressions régulières.

Langages

La détection se fait par extension de fichier — ou par nom exact, pour les fichiers dont l'identité est le nom : Makefile (et *.mk) et les messages de git (COMMIT_EDITMSG, MERGE_MSG, TAG_EDITMSG), si bien que les messages de commit écrits via chum --wait arrivent colorés. Elle s'exécute à l'ouverture et au premier enregistrement d'un document sans titre. Sont actuellement reconnus : Rust, Python, Go, C, C++, TypeScript, TSX, JavaScript, Java, C#, Ruby, PHP, Lua, Haskell, OCaml, Scala, Elixir, Nix, R, CMake, XML, JSON, Bash, Make, messages de commit git, modèles Go, HTML, CSS, TOML, YAML, SQL, Swift, Zig, Markdown, HCL et Dockerfiles. HCL couvre Terraform, Terragrunt, Packer et Nomad (.tf, .tfvars, .hcl, .nomad, .pkr), le mot qui ouvre un bloc étant coloré comme mot-clé quel que soit le dialecte ; un Dockerfile se reconnaît à son nom, suffixé ou non — Dockerfile, Dockerfile.dev, api.Dockerfile, Containerfile. Les artefacts AWS qui les entourent — modèles CloudFormation et SAM, définitions de tâches ECS, politiques IAM, buildspec.yml, appspec.yml, .tfstate — sont du JSON ou du YAML et sont colorés comme tels ; les balises qu'ajoute CloudFormation (!Ref, !GetAtt) sont celles de YAML. C++ et TypeScript héritent de la coloration de leur parent — celle de C et de JavaScript — et ajoutent la leur par-dessus, ce qui est la façon dont leurs grammaires sont fournies : chacune ne porte que ce qu'elle ajoute. Le sous-titre de la fenêtre affiche le langage actif ; les fichiers non reconnus restent simplement en texte brut. Les lignes du navigateur portent l'icône Finder propre au type quand macOS le distingue vraiment — et un petit insigne à la couleur conventionnelle du langage sinon. La distinction compte : une application par défaut (un IDE, disons) estampille son propre icône de document sur tous les types qu'elle revendique, identique partout ; une icône partagée entre types compte donc comme générique et l'insigne l'emporte.

Les grammaires sont compilées dans l'application : la coloration fonctionne hors ligne et à l'identique partout.

Injections

Les documents qui incorporent d'autres langages colorent le contenu incorporé avec la grammaire du langage incorporé :

  • Les blocs de code clôturés de Markdown sont colorés selon le langage nommé sur la clôture (```rust et consorts), et l'emphase, les liens et le code en ligne de Markdown viennent d'une grammaire dédiée aux éléments en ligne.
  • Les éléments <script> et <style> de HTML se colorent comme du JavaScript et du CSS.

Crochets

Placez le curseur sur un (, [ ou { — ou juste après sa fermeture — et les deux bouts de la paire sont teintés, pour trouver l'autre sans compter. Un crochet dans une chaîne ou un commentaire n'est pas un crochet, et un crochet sans partenaire est laissé tranquille : rien n'est teinté, et c'est la réponse.

Colour bracket pairs by depth (Réglages ▸ Général, désactivé par défaut) peint chaque crochet apparié dans l'une de six couleurs selon sa profondeur d'imbrication, en recommençant après la sixième, par- dessus la couleur syntaxique. Un crochet sans partenaire garde sa couleur syntaxique, ce qui le fait ressortir. Les documents de plus d'un million de caractères n'ont ni l'un ni l'autre : le balayage couvre tout le texte et ses chaînes, à chaque modification.

Depuis le serveur de langage

La grammaire connaît la forme du texte ; un serveur de langage sait ce qu'est chaque nom. Quand le serveur d'un fichier colore les noms — rust-analyzer le fait, et gopls quand on le lui demande, ce que fait le preset Go — sa parole est peinte par-dessus celle de la grammaire, sous les mêmes noms que le thème colore déjà. Un paramètre, une constante ou un type de la bibliothèque prend sa propre couleur partout où il est utilisé, pas seulement là où la grammaire sait le distinguer. Le serveur est interrogé quand la frappe s'arrête ; tant qu'il n'a pas répondu, un nom tout juste modifié garde la couleur de la grammaire, et un genre de nom pour lequel aucun thème n'a de couleur reste à la grammaire.

Comment ça marche

Le partage des rôles suit la règle architecturale du projet :

  • Le noyau possède l'analyse. Chaque édition transmet à l'arbre une description exacte du changement, et tree-sitter réanalyse de façon incrémentale — un travail à l'échelle de la frappe, quelle que soit la taille du fichier. À la demande, il exécute la requête de coloration du langage sur une plage et répond par des segments stylés : des plages plus des indices dans une table de styles.
  • La coque possède les pixels. Les segments sont peints comme attributs de rendu TextKit — une surcouche couleur seule qui ne peut pas invalider la mise en page du texte, si bien que colorer ne concurrence jamais la frappe.

La table de styles porte une couleur par apparence du système : passer du mode clair au mode sombre recolore instantanément, avec des palettes réglées pour chacun.

Les très grands documents (au-delà de quelques mégaoctets) sautent délibérément la coloration ; l'éditeur lui-même reste rapide à toute taille.

La palette elle-même est un thème — sept sont fournis d'origine, et les thèmes de l'utilisateur sont des fichiers JSON ; voir les thèmes dans la configuration.

Si un artefact de coloration survivait à une édition, View → Redraw (⌥⌘L, réassignable comme redraw) reconstruit chaque couche visuelle depuis zéro : attributs de base, couleurs de syntaxe, marques de diagnostic et la marge.

La coloration suit la zone visible : c'est elle, plus une marge généreuse, qui est interrogée et peinte, puis repeinte au défilement. Un fichier d'un mégaoctet coûte autant qu'un petit, et il n'existe plus de taille au-delà de laquelle la couleur s'arrête en silence — sauf le plafond d'analyse du cœur, au-delà duquel un document est du texte brut par choix.

Le gras et l'italique d'un thème sont honorés aussi. La couleur passe par les attributs de rendu de TextKit, qui ne touchent pas à la mise en page ; les traits typographiques sont appliqués comme polices, d'où leur peinture sur la portion visible plutôt que sur tout le document. Les fontes à chasse fixe gardent leur largeur d'une graisse à l'autre, donc rien ne se replace.

Pas encore là

  • Les six couleurs de l'arc-en-ciel sont celles de l'éditeur, pas du thème ; un thème ne peut pas encore les changer.
  • Un serveur qui ne colore qu'une partie d'un fichier à la fois n'est pas interrogé ; le fichier entier est demandé après chaque pause dans la frappe, et la coloration du serveur ne peut pas être désactivée à part.
  • Les requêtes limitées à la portion visible pour les documents de plusieurs centaines de kilooctets : aujourd'hui ils sont colorés en entier ou, au-delà d'un plafond, pas du tout.